Merge "Add an __ANDROID_RAMDISK__ preprocessor define."
diff --git a/Android.bp b/Android.bp
index 0e8d86d..7c50047 100644
--- a/Android.bp
+++ b/Android.bp
@@ -46,25 +46,6 @@
 // C static libraries extracted from the gcc toolchain
 //
 
-toolchain_library {
-    name: "libwinpthread",
-    host_supported: true,
-    enabled: false,
-    target: {
-        windows: {
-            enabled: true,
-        },
-        windows_x86: {
-            src: "prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/lib32/libwinpthread.a",
-        },
-        windows_x86_64: {
-            src: "prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/lib/libwinpthread.a",
-        },
-    },
-    notice: ":mingw-libwinpthread-notice",
-    licenses: ["winpthreads_license"],
-}
-
 kernel_headers {
     name: "device_kernel_headers",
     vendor: true,
diff --git a/OWNERS b/OWNERS
index f0ccd82..937a243 100644
--- a/OWNERS
+++ b/OWNERS
@@ -7,12 +7,15 @@
 asmundak@google.com
 ccross@android.com
 cparsons@google.com
+delmerico@google.com
 dwillemsen@google.com
 eakammer@google.com
 jobredeaux@google.com
 joeo@google.com
 lamontjones@google.com
 spandandas@google.com
+tradical@google.com
+usta@google.com
 weiwli@google.com
 yudiliu@google.com
 yuntaoxu@google.com
diff --git a/README.md b/README.md
index b820fd1..caffd3d 100644
--- a/README.md
+++ b/README.md
@@ -188,7 +188,7 @@
 
 #### Namespaces
 
-A presense of the `soong_namespace {..}` in an Android.bp file defines a
+The presence of the `soong_namespace {..}` in an Android.bp file defines a
 **namespace**. For instance, having
 
 ```
@@ -452,10 +452,9 @@
 
 The values of the variables can be set from a product's `BoardConfig.mk` file:
 ```
-$(call add_soong_config_namespace, acme)
-$(call add_soong_config_var_value, acme, board, soc_a)
-$(call add_soong_config_var_value, acme, feature, true)
-$(call add_soong_config_var_value, acme, width, 200)
+$(call soong_config_set,acme,board,soc_a)
+$(call soong_config_set,acme,feature,true)
+$(call soong_config_set,acme,width,200)
 ```
 
 The `acme_cc_defaults` module type can be used anywhere after the definition in
@@ -551,6 +550,26 @@
 and produces build rules.  The build rules are collected by blueprint and
 written to a [ninja](http://ninja-build.org) build file.
 
+## Environment Variables Config File
+
+Soong can optionally load environment variables from a pre-specified
+configuration file during startup. These environment variables can be used
+to control the behavior of the build. For example, these variables can determine
+whether remote-execution should be used for the build or not.
+
+The `ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR` environment variable specifies the
+directory in which the config file should be searched for. The
+`ANDROID_BUILD_ENVIRONMENT_CONFIG` variable determines the name of the config
+file to be searched for within the config directory. For example, the following
+build comand will load `ENV_VAR_1` and `ENV_VAR_2` environment variables from
+the `example_config.json` file inside the `build/soong` directory.
+
+```
+ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR=build/soong \
+  ANDROID_BUILD_ENVIRONMENT_CONFIG=example_config \
+  build/soong/soong_ui.bash
+```
+
 ## Other documentation
 
 * [Best Practices](docs/best_practices.md)
diff --git a/android/Android.bp b/android/Android.bp
index a20aedc..d3540b2 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -16,7 +16,9 @@
         "soong-remoteexec",
         "soong-response",
         "soong-shared",
+        "soong-starlark-format",
         "soong-ui-metrics_proto",
+
         "golang-protobuf-proto",
         "golang-protobuf-encoding-prototext",
 
@@ -47,6 +49,7 @@
         "image.go",
         "license.go",
         "license_kind.go",
+        "license_metadata.go",
         "license_sdk_member.go",
         "licenses.go",
         "makefile_goal.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 0adc2a6..72b6584 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -474,6 +474,7 @@
 	ModuleDir(module blueprint.Module) string
 	Config() Config
 	ModuleProvider(module blueprint.Module, provider blueprint.ProviderKey) interface{}
+	ModuleHasProvider(module blueprint.Module, provider blueprint.ProviderKey) bool
 }
 
 func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
@@ -517,7 +518,7 @@
 	a.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
 
 	// If the install rule was generated by Soong tell Make about it.
-	if amod.InstallBypassMake() && len(base.katiInstalls) > 0 {
+	if len(base.katiInstalls) > 0 {
 		// Assume the primary install file is last since it probably needs to depend on any other
 		// installed files.  If that is not the case we can add a method to specify the primary
 		// installed file.
@@ -609,6 +610,11 @@
 		}
 	}
 
+	if ctx.ModuleHasProvider(mod, LicenseMetadataProvider) {
+		licenseMetadata := ctx.ModuleProvider(mod, LicenseMetadataProvider).(*LicenseMetadataInfo)
+		a.SetPath("LOCAL_SOONG_LICENSE_METADATA", licenseMetadata.LicenseMetadataPath)
+	}
+
 	extraCtx := &androidMkExtraEntriesContext{
 		ctx: ctx,
 		mod: mod,
@@ -887,6 +893,10 @@
 	return nil
 }
 
+func ShouldSkipAndroidMkProcessing(module Module) bool {
+	return shouldSkipAndroidMkProcessing(module.base())
+}
+
 func shouldSkipAndroidMkProcessing(module *ModuleBase) bool {
 	if !module.commonProperties.NamespaceExportedToMake {
 		// TODO(jeffrygaston) do we want to validate that there are no modules being
diff --git a/android/apex.go b/android/apex.go
index 461faf7..cf1bcfe 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -908,16 +908,18 @@
 //
 // Return true if the `to` module should be visited, false otherwise.
 type PayloadDepsCallback func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool
+type WalkPayloadDepsFunc func(ctx ModuleContext, do PayloadDepsCallback)
 
-// UpdatableModule represents updatable APEX/APK
-type UpdatableModule interface {
+// ModuleWithMinSdkVersionCheck represents a module that implements min_sdk_version checks
+type ModuleWithMinSdkVersionCheck interface {
 	Module
-	WalkPayloadDeps(ctx ModuleContext, do PayloadDepsCallback)
+	MinSdkVersion(ctx EarlyModuleContext) SdkSpec
+	CheckMinSdkVersion(ctx ModuleContext)
 }
 
 // CheckMinSdkVersion checks if every dependency of an updatable module sets min_sdk_version
 // accordingly
-func CheckMinSdkVersion(m UpdatableModule, ctx ModuleContext, minSdkVersion ApiLevel) {
+func CheckMinSdkVersion(ctx ModuleContext, minSdkVersion ApiLevel, walk WalkPayloadDepsFunc) {
 	// do not enforce min_sdk_version for host
 	if ctx.Host() {
 		return
@@ -933,7 +935,7 @@
 		return
 	}
 
-	m.WalkPayloadDeps(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
+	walk(ctx, func(ctx ModuleContext, from blueprint.Module, to ApexModule, externalDep bool) bool {
 		if externalDep {
 			// external deps are outside the payload boundary, which is "stable"
 			// interface. We don't have to check min_sdk_version for external
@@ -943,6 +945,14 @@
 		if am, ok := from.(DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
 			return false
 		}
+		if m, ok := to.(ModuleWithMinSdkVersionCheck); ok {
+			// This dependency performs its own min_sdk_version check, just make sure it sets min_sdk_version
+			// to trigger the check.
+			if !m.MinSdkVersion(ctx).Specified() {
+				ctx.OtherModuleErrorf(m, "must set min_sdk_version")
+			}
+			return false
+		}
 		if err := to.ShouldSupportSdkVersion(ctx, minSdkVersion); err != nil {
 			toName := ctx.OtherModuleName(to)
 			if ver, ok := minSdkVersionAllowlist[toName]; !ok || ver.GreaterThan(minSdkVersion) {
diff --git a/android/api_levels.go b/android/api_levels.go
index 1fbbc15..926d297 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -344,7 +344,7 @@
 
 var apiLevelsMapKey = NewOnceKey("ApiLevelsMap")
 
-func getApiLevelsMap(config Config) map[string]int {
+func GetApiLevelsMap(config Config) map[string]int {
 	return config.Once(apiLevelsMapKey, func() interface{} {
 		apiLevelsMap := map[string]int{
 			"G":     9,
@@ -374,7 +374,7 @@
 }
 
 func (a *apiLevelsSingleton) GenerateBuildActions(ctx SingletonContext) {
-	apiLevelsMap := getApiLevelsMap(ctx.Config())
+	apiLevelsMap := GetApiLevelsMap(ctx.Config())
 	apiLevelsJson := GetApiLevelsJson(ctx)
 	createApiLevelsJson(ctx, apiLevelsJson, apiLevelsMap)
 }
diff --git a/android/arch.go b/android/arch.go
index 3bf54b7..a719cf3 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -168,7 +168,7 @@
 	return archType
 }
 
-// ArchTypeList returns the a slice copy of the 4 supported ArchTypes for arm,
+// ArchTypeList returns a slice copy of the 4 supported ArchTypes for arm,
 // arm64, x86 and x86_64.
 func ArchTypeList() []ArchType {
 	return append([]ArchType(nil), archTypeList...)
@@ -408,7 +408,7 @@
 
 	// addPathDepsForProps does not descend into sub structs, so we need to descend into the
 	// arch-specific properties ourselves
-	properties := []interface{}{}
+	var properties []interface{}
 	for _, archProperties := range m.archProperties {
 		for _, archProps := range archProperties {
 			archPropValues := reflect.ValueOf(archProps).Elem()
@@ -566,6 +566,8 @@
 	return variants
 }
 
+var DarwinUniversalVariantTag = archDepTag{name: "darwin universal binary"}
+
 // archMutator splits a module into a variant for each Target requested by the module.  Target selection
 // for a module is in three levels, OsClass, multilib, and then Target.
 // OsClass selection is determined by:
@@ -652,7 +654,7 @@
 	prefer32 := os == Windows
 
 	// Determine the multilib selection for this module.
-	multilib, extraMultilib := decodeMultilib(base, os.Class)
+	multilib, extraMultilib := decodeMultilib(base, os)
 
 	// Convert the multilib selection into a list of Targets.
 	targets, err := decodeMultilibTargets(multilib, osTargets, prefer32)
@@ -702,6 +704,16 @@
 			m.base().commonProperties.SkipInstall = true
 		}
 	}
+
+	// Create a dependency for Darwin Universal binaries from the primary to secondary
+	// architecture. The module itself will be responsible for calling lipo to merge the outputs.
+	if os == Darwin {
+		if multilib == "darwin_universal" && len(modules) == 2 {
+			mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[1], modules[0])
+		} else if multilib == "darwin_universal_common_first" && len(modules) == 3 {
+			mctx.AddInterVariantDependency(DarwinUniversalVariantTag, modules[2], modules[1])
+		}
+	}
 }
 
 // addTargetProperties annotates a variant with the Target is is being compiled for, the list
@@ -717,9 +729,9 @@
 // multilib from the factory's call to InitAndroidArchModule if none was set.  For modules that
 // called InitAndroidMultiTargetsArchModule it always returns "common" for multilib, and returns
 // the actual multilib in extraMultilib.
-func decodeMultilib(base *ModuleBase, class OsClass) (multilib, extraMultilib string) {
+func decodeMultilib(base *ModuleBase, os OsType) (multilib, extraMultilib string) {
 	// First check the "android.compile_multilib" or "host.compile_multilib" properties.
-	switch class {
+	switch os.Class {
 	case Device:
 		multilib = String(base.commonProperties.Target.Android.Compile_multilib)
 	case Host:
@@ -737,6 +749,26 @@
 	}
 
 	if base.commonProperties.UseTargetVariants {
+		// Darwin has the concept of "universal binaries" which is implemented in Soong by
+		// building both x86_64 and arm64 variants, and having select module types know how to
+		// merge the outputs of their corresponding variants together into a final binary. Most
+		// module types don't need to understand this logic, as we only build a small portion
+		// of the tree for Darwin, and only module types writing macho files need to do the
+		// merging.
+		//
+		// This logic is not enabled for:
+		//  "common", as it's not an arch-specific variant
+		//  "32", as Darwin never has a 32-bit variant
+		//  !UseTargetVariants, as the module has opted into handling the arch-specific logic on
+		//    its own.
+		if os == Darwin && multilib != "common" && multilib != "32" {
+			if multilib == "common_first" {
+				multilib = "darwin_universal_common_first"
+			} else {
+				multilib = "darwin_universal"
+			}
+		}
+
 		return multilib, ""
 	} else {
 		// For app modules a single arch variant will be created per OS class which is expected to handle all the
@@ -885,7 +917,8 @@
 			for _, archType := range osArchTypeMap[os] {
 				targets = append(targets, GetCompoundTargetField(os, archType))
 
-				// Also add the special "linux_<arch>" and "bionic_<arch>" property structs.
+				// Also add the special "linux_<arch>", "bionic_<arch>" , "glibc_<arch>", and
+				// "musl_<arch>" property structs.
 				if os.Linux() {
 					target := "Linux_" + archType.Name
 					if !InList(target, targets) {
@@ -898,6 +931,18 @@
 						targets = append(targets, target)
 					}
 				}
+				if os == Linux {
+					target := "Glibc_" + archType.Name
+					if !InList(target, targets) {
+						targets = append(targets, target)
+					}
+				}
+				if os == LinuxMusl {
+					target := "Musl_" + archType.Name
+					if !InList(target, targets) {
+						targets = append(targets, target)
+					}
+				}
 			}
 		}
 
@@ -961,10 +1006,11 @@
 
 	base := m.base()
 
-	// Store the original list of top level property structs
-	base.generalProperties = m.GetProperties()
+	if len(base.archProperties) != 0 {
+		panic(fmt.Errorf("module %s already has archProperties", m.Name()))
+	}
 
-	for _, properties := range base.generalProperties {
+	getStructType := func(properties interface{}) reflect.Type {
 		propertiesValue := reflect.ValueOf(properties)
 		t := propertiesValue.Type()
 		if propertiesValue.Kind() != reflect.Ptr {
@@ -974,10 +1020,14 @@
 
 		propertiesValue = propertiesValue.Elem()
 		if propertiesValue.Kind() != reflect.Struct {
-			panic(fmt.Errorf("properties must be a pointer to a struct, got %T",
+			panic(fmt.Errorf("properties must be a pointer to a struct, got a pointer to %T",
 				propertiesValue.Interface()))
 		}
+		return t
+	}
 
+	for _, properties := range m.GetProperties() {
+		t := getStructType(properties)
 		// Get or create the arch-specific property struct types for this property struct type.
 		archPropTypes := archPropTypeMap.Once(NewCustomOnceKey(t), func() interface{} {
 			return createArchPropTypeDesc(t)
@@ -997,9 +1047,6 @@
 		m.AddProperties(archProperties...)
 	}
 
-	// Update the list of properties that can be set by a defaults module or a call to
-	// AppendMatchingProperties or PrependMatchingProperties.
-	base.customizableProperties = m.GetProperties()
 }
 
 func maybeBlueprintEmbed(src reflect.Value) reflect.Value {
@@ -1072,8 +1119,8 @@
 func (m *ModuleBase) setOSProperties(ctx BottomUpMutatorContext) {
 	os := m.commonProperties.CompileOS
 
-	for i := range m.generalProperties {
-		genProps := m.generalProperties[i]
+	for i := range m.archProperties {
+		genProps := m.GetProperties()[i]
 		if m.archProperties[i] == nil {
 			continue
 		}
@@ -1345,11 +1392,25 @@
 			result = append(result, osArchProperties)
 		}
 
+		if os == Linux {
+			field := "Glibc_" + archType.Name
+			userFriendlyField := "target.glibc_" + "_" + archType.Name
+			if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+				result = append(result, osArchProperties)
+			}
+		}
+
 		if os == LinuxMusl {
+			field := "Musl_" + archType.Name
+			userFriendlyField := "target.musl_" + "_" + archType.Name
+			if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+				result = append(result, osArchProperties)
+			}
+
 			// Special case:  to ease the transition from glibc to musl, apply linux_glibc
 			// properties (which has historically mean host linux) to musl variants.
-			field := "Linux_glibc_" + archType.Name
-			userFriendlyField := "target.linux_glibc_" + archType.Name
+			field = "Linux_glibc_" + archType.Name
+			userFriendlyField = "target.linux_glibc_" + archType.Name
 			if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
 				result = append(result, osArchProperties)
 			}
@@ -1400,8 +1461,8 @@
 	arch := m.Arch()
 	os := m.Os()
 
-	for i := range m.generalProperties {
-		genProps := m.generalProperties[i]
+	for i := range m.archProperties {
+		genProps := m.GetProperties()[i]
 		if m.archProperties[i] == nil {
 			continue
 		}
@@ -1793,6 +1854,15 @@
 		if len(buildTargets) == 0 {
 			buildTargets = filterMultilibTargets(targets, "lib64")
 		}
+	case "darwin_universal":
+		buildTargets = filterMultilibTargets(targets, "lib64")
+		// Reverse the targets so that the first architecture can depend on the second
+		// architecture module in order to merge the outputs.
+		reverseSliceInPlace(buildTargets)
+	case "darwin_universal_common_first":
+		archTargets := filterMultilibTargets(targets, "lib64")
+		reverseSliceInPlace(archTargets)
+		buildTargets = append(getCommonTargets(targets), archTargets...)
 	default:
 		return nil, fmt.Errorf(`compile_multilib must be "both", "first", "32", "64", "prefer32" or "first_prefer32" found %q`,
 			multilib)
@@ -1970,8 +2040,8 @@
 	var archProperties []interface{}
 
 	// First find the property set in the module that corresponds to the requested
-	// one. m.archProperties[i] corresponds to m.generalProperties[i].
-	for i, generalProp := range m.generalProperties {
+	// one. m.archProperties[i] corresponds to m.GetProperties()[i].
+	for i, generalProp := range m.GetProperties() {
 		srcType := reflect.ValueOf(generalProp).Type()
 		if srcType == dstType {
 			archProperties = m.archProperties[i]
diff --git a/android/bazel.go b/android/bazel.go
index 40c971f..f4fc038 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"bufio"
+	"errors"
 	"fmt"
 	"io/ioutil"
 	"path/filepath"
@@ -39,6 +41,10 @@
 	// To opt-out a module, set bazel_module: { bp2build_available: false }
 	// To defer the default setting for the directory, do not set the value.
 	Bp2build_available *bool
+
+	// CanConvertToBazel is set via InitBazelModule to indicate that a module type can be converted to
+	// Bazel with Bp2build.
+	CanConvertToBazel bool `blueprint:"mutated"`
 }
 
 // Properties contains common module properties for Bazel migration purposes.
@@ -80,9 +86,10 @@
 	HasHandcraftedLabel() bool
 	HandcraftedLabel() string
 	GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
-	ConvertWithBp2build(ctx BazelConversionContext) bool
-	convertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool
+	ShouldConvertWithBp2build(ctx BazelConversionContext) bool
+	shouldConvertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool
 	GetBazelBuildFileContents(c Config, path, name string) (string, error)
+	ConvertWithBp2build(ctx TopDownMutatorContext)
 
 	// namespacedVariableProps is a map from a soong config variable namespace
 	// (e.g. acme, android) to a map of interfaces{}, which are really
@@ -109,6 +116,7 @@
 // properties.
 func InitBazelModule(module BazelModule) {
 	module.AddProperties(module.bazelProps())
+	module.bazelProps().Bazel_module.CanConvertToBazel = true
 }
 
 // bazelProps returns the Bazel properties for the given BazelModuleBase.
@@ -147,7 +155,7 @@
 	if b.HasHandcraftedLabel() {
 		return b.HandcraftedLabel()
 	}
-	if b.ConvertWithBp2build(ctx) {
+	if b.ShouldConvertWithBp2build(ctx) {
 		return bp2buildModuleLabel(ctx, module)
 	}
 	return "" // no label for unconverted module
@@ -190,6 +198,7 @@
 		// build/bazel is not recursive. Instead list each subdirectory under
 		// build/bazel explicitly.
 		"build/bazel":/* recursive = */ false,
+		"build/bazel/ci/dist":/* recursive = */ false,
 		"build/bazel/examples/android_app":/* recursive = */ true,
 		"build/bazel/examples/java":/* recursive = */ true,
 		"build/bazel/bazel_skylib":/* recursive = */ true,
@@ -200,7 +209,8 @@
 		"build/bazel/platforms":/* recursive = */ true,
 		"build/bazel/product_variables":/* recursive = */ true,
 		"build/bazel_common_rules":/* recursive = */ true,
-		"build/make/tools":/* recursive = */ true,
+		// build/make/tools/signapk BUILD file is generated, so build/make/tools is not recursive.
+		"build/make/tools":/* recursive = */ false,
 		"build/pesto":/* recursive = */ true,
 
 		// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
@@ -208,7 +218,6 @@
 		"external/bazelbuild-rules_android":/* recursive = */ true,
 		"external/bazel-skylib":/* recursive = */ true,
 		"external/guava":/* recursive = */ true,
-		"external/error_prone":/* recursive = */ true,
 		"external/jsr305":/* recursive = */ true,
 		"frameworks/ex/common":/* recursive = */ true,
 
@@ -217,6 +226,7 @@
 		"packages/apps/WallpaperPicker":/* recursive = */ false,
 
 		"prebuilts/gcc":/* recursive = */ true,
+		"prebuilts/build-tools":/* recursive = */ false,
 		"prebuilts/sdk":/* recursive = */ false,
 		"prebuilts/sdk/current/extras/app-toolkit":/* recursive = */ false,
 		"prebuilts/sdk/current/support":/* recursive = */ false,
@@ -226,27 +236,81 @@
 
 	// Configure modules in these directories to enable bp2build_available: true or false by default.
 	bp2buildDefaultConfig = Bp2BuildConfig{
-		"art/libdexfile": Bp2BuildDefaultTrueRecursively,
-		"bionic":         Bp2BuildDefaultTrueRecursively,
+		"art/libartpalette":                     Bp2BuildDefaultTrueRecursively,
+		"art/libdexfile":                        Bp2BuildDefaultTrueRecursively,
+		"art/runtime":                           Bp2BuildDefaultTrueRecursively,
+		"art/tools":                             Bp2BuildDefaultTrue,
+		"bionic":                                Bp2BuildDefaultTrueRecursively,
+		"bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
 		"build/bazel/examples/soong_config_variables":        Bp2BuildDefaultTrueRecursively,
 		"build/bazel/examples/apex/minimal":                  Bp2BuildDefaultTrueRecursively,
+		"build/make/tools/signapk":                           Bp2BuildDefaultTrue,
 		"build/soong":                                        Bp2BuildDefaultTrue,
 		"build/soong/cc/libbuildversion":                     Bp2BuildDefaultTrue, // Skip tests subdir
+		"build/soong/cc/ndkstubgen":                          Bp2BuildDefaultTrue,
+		"build/soong/cc/symbolfile":                          Bp2BuildDefaultTrue,
+		"build/soong/linkerconfig":                           Bp2BuildDefaultTrueRecursively,
+		"build/soong/scripts":                                Bp2BuildDefaultTrueRecursively,
 		"cts/common/device-side/nativetesthelper/jni":        Bp2BuildDefaultTrueRecursively,
+		"development/apps/DevelopmentSettings":               Bp2BuildDefaultTrue,
+		"development/apps/Fallback":                          Bp2BuildDefaultTrue,
+		"development/apps/WidgetPreview":                     Bp2BuildDefaultTrue,
+		"development/samples/BasicGLSurfaceView":             Bp2BuildDefaultTrue,
+		"development/samples/BluetoothChat":                  Bp2BuildDefaultTrue,
+		"development/samples/BrokenKeyDerivation":            Bp2BuildDefaultTrue,
+		"development/samples/Compass":                        Bp2BuildDefaultTrue,
+		"development/samples/ContactManager":                 Bp2BuildDefaultTrue,
+		"development/samples/FixedGridLayout":                Bp2BuildDefaultTrue,
+		"development/samples/HelloEffects":                   Bp2BuildDefaultTrue,
+		"development/samples/Home":                           Bp2BuildDefaultTrue,
+		"development/samples/HoneycombGallery":               Bp2BuildDefaultTrue,
+		"development/samples/JetBoy":                         Bp2BuildDefaultTrue,
+		"development/samples/KeyChainDemo":                   Bp2BuildDefaultTrue,
+		"development/samples/LceDemo":                        Bp2BuildDefaultTrue,
+		"development/samples/LunarLander":                    Bp2BuildDefaultTrue,
+		"development/samples/MultiResolution":                Bp2BuildDefaultTrue,
+		"development/samples/MultiWindow":                    Bp2BuildDefaultTrue,
+		"development/samples/NotePad":                        Bp2BuildDefaultTrue,
+		"development/samples/Obb":                            Bp2BuildDefaultTrue,
+		"development/samples/RSSReader":                      Bp2BuildDefaultTrue,
+		"development/samples/ReceiveShareDemo":               Bp2BuildDefaultTrue,
+		"development/samples/SearchableDictionary":           Bp2BuildDefaultTrue,
+		"development/samples/SipDemo":                        Bp2BuildDefaultTrue,
+		"development/samples/SkeletonApp":                    Bp2BuildDefaultTrue,
+		"development/samples/Snake":                          Bp2BuildDefaultTrue,
+		"development/samples/SpellChecker/":                  Bp2BuildDefaultTrueRecursively,
+		"development/samples/ThemedNavBarKeyboard":           Bp2BuildDefaultTrue,
+		"development/samples/ToyVpn":                         Bp2BuildDefaultTrue,
+		"development/samples/TtsEngine":                      Bp2BuildDefaultTrue,
+		"development/samples/USB/AdbTest":                    Bp2BuildDefaultTrue,
+		"development/samples/USB/MissileLauncher":            Bp2BuildDefaultTrue,
+		"development/samples/VoiceRecognitionService":        Bp2BuildDefaultTrue,
+		"development/samples/VoicemailProviderDemo":          Bp2BuildDefaultTrue,
+		"development/samples/WiFiDirectDemo":                 Bp2BuildDefaultTrue,
 		"development/sdk":                                    Bp2BuildDefaultTrueRecursively,
 		"external/arm-optimized-routines":                    Bp2BuildDefaultTrueRecursively,
+		"external/auto/common":                               Bp2BuildDefaultTrueRecursively,
+		"external/auto/service":                              Bp2BuildDefaultTrueRecursively,
 		"external/boringssl":                                 Bp2BuildDefaultTrueRecursively,
+		"external/bouncycastle":                              Bp2BuildDefaultTrue,
 		"external/brotli":                                    Bp2BuildDefaultTrue,
+		"external/conscrypt":                                 Bp2BuildDefaultTrue,
+		"external/error_prone":                               Bp2BuildDefaultTrueRecursively,
 		"external/fmtlib":                                    Bp2BuildDefaultTrueRecursively,
 		"external/google-benchmark":                          Bp2BuildDefaultTrueRecursively,
 		"external/googletest":                                Bp2BuildDefaultTrueRecursively,
 		"external/gwp_asan":                                  Bp2BuildDefaultTrueRecursively,
+		"external/icu":                                       Bp2BuildDefaultTrueRecursively,
+		"external/icu/android_icu4j":                         Bp2BuildDefaultFalse, // java rules incomplete
+		"external/icu/icu4j":                                 Bp2BuildDefaultFalse, // java rules incomplete
+		"external/javapoet":                                  Bp2BuildDefaultTrueRecursively,
 		"external/jemalloc_new":                              Bp2BuildDefaultTrueRecursively,
 		"external/jsoncpp":                                   Bp2BuildDefaultTrueRecursively,
 		"external/libcap":                                    Bp2BuildDefaultTrueRecursively,
 		"external/libcxx":                                    Bp2BuildDefaultTrueRecursively,
 		"external/libcxxabi":                                 Bp2BuildDefaultTrueRecursively,
 		"external/libevent":                                  Bp2BuildDefaultTrueRecursively,
+		"external/libpng":                                    Bp2BuildDefaultTrueRecursively,
 		"external/lz4/lib":                                   Bp2BuildDefaultTrue,
 		"external/lzma/C":                                    Bp2BuildDefaultTrueRecursively,
 		"external/mdnsresponder":                             Bp2BuildDefaultTrueRecursively,
@@ -254,24 +318,42 @@
 		"external/pcre":                                      Bp2BuildDefaultTrueRecursively,
 		"external/protobuf":                                  Bp2BuildDefaultTrueRecursively,
 		"external/python/six":                                Bp2BuildDefaultTrueRecursively,
-		"external/selinux/libsepol":                          Bp2BuildDefaultTrueRecursively,
 		"external/scudo":                                     Bp2BuildDefaultTrueRecursively,
 		"external/selinux/libselinux":                        Bp2BuildDefaultTrueRecursively,
+		"external/selinux/libsepol":                          Bp2BuildDefaultTrueRecursively,
 		"external/zlib":                                      Bp2BuildDefaultTrueRecursively,
 		"external/zstd":                                      Bp2BuildDefaultTrueRecursively,
+		"frameworks/base/media/tests/MediaDump":              Bp2BuildDefaultTrue,
+		"frameworks/base/startop/apps/test":                  Bp2BuildDefaultTrue,
 		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
+		"frameworks/native/opengl/tests/gl2_cameraeye":       Bp2BuildDefaultTrue,
+		"frameworks/native/opengl/tests/gl2_java":            Bp2BuildDefaultTrue,
+		"frameworks/native/opengl/tests/testLatency":         Bp2BuildDefaultTrue,
+		"frameworks/native/opengl/tests/testPauseResume":     Bp2BuildDefaultTrue,
+		"frameworks/native/opengl/tests/testViewport":        Bp2BuildDefaultTrue,
 		"frameworks/proto_logging/stats/stats_log_api_gen":   Bp2BuildDefaultTrueRecursively,
 		"libnativehelper":                                    Bp2BuildDefaultTrueRecursively,
+		"packages/apps/DevCamera":                            Bp2BuildDefaultTrue,
+		"packages/apps/HTMLViewer":                           Bp2BuildDefaultTrue,
+		"packages/apps/Protips":                              Bp2BuildDefaultTrue,
+		"packages/modules/StatsD/lib/libstatssocket":         Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb":                               Bp2BuildDefaultTrue,
+		"packages/modules/adb/apex":                          Bp2BuildDefaultTrue,
 		"packages/modules/adb/crypto":                        Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb/libs":                          Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb/pairing_auth":                  Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb/pairing_connection":            Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb/proto":                         Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb/tls":                           Bp2BuildDefaultTrueRecursively,
+		"packages/providers/MediaProvider/tools/dialogs":     Bp2BuildDefaultTrue,
+		"packages/screensavers/Basic":                        Bp2BuildDefaultTrue,
+		"packages/services/Car/tests/SampleRearViewCamera":   Bp2BuildDefaultTrue,
 		"prebuilts/clang/host/linux-x86":                     Bp2BuildDefaultTrueRecursively,
+		"prebuilts/tools/common/m2":                          Bp2BuildDefaultTrue,
 		"system/apex":                                        Bp2BuildDefaultFalse, // TODO(b/207466993): flaky failures
-		"system/core/debuggerd":                              Bp2BuildDefaultTrue,
+		"system/apex/proto":                                  Bp2BuildDefaultTrueRecursively,
+		"system/apex/libs":                                   Bp2BuildDefaultTrueRecursively,
+		"system/core/debuggerd":                              Bp2BuildDefaultTrueRecursively,
 		"system/core/diagnose_usb":                           Bp2BuildDefaultTrueRecursively,
 		"system/core/libasyncio":                             Bp2BuildDefaultTrue,
 		"system/core/libcrypto_utils":                        Bp2BuildDefaultTrueRecursively,
@@ -293,28 +375,44 @@
 		"system/timezone/output_data":                        Bp2BuildDefaultTrueRecursively,
 		"system/unwinding/libbacktrace":                      Bp2BuildDefaultTrueRecursively,
 		"system/unwinding/libunwindstack":                    Bp2BuildDefaultTrueRecursively,
+		"tools/apksig":                                       Bp2BuildDefaultTrue,
+		"tools/platform-compat/java/android/compat":          Bp2BuildDefaultTrueRecursively,
 	}
 
 	// Per-module denylist to always opt modules out of both bp2build and mixed builds.
 	bp2buildModuleDoNotConvertList = []string{
-		"libnativehelper_compat_libc++",              // Broken compile: implicit declaration of function 'strerror_r' is invalid in C99
-		"art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found
+		"libnativehelper_compat_libc", // Broken compile: implicit declaration of function 'strerror_r' is invalid in C99
+
+		"libart",                             // depends on unconverted modules: art_operator_srcs, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libcpu_features, libdexfile, libartpalette, libbacktrace, libnativebridge, libnativeloader, libsigchain, libunwindstack, libartbase, libprofile, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, libstatssocket, heapprofd_client_api
+		"libart-runtime-gtest",               // depends on unconverted modules: libgtest_isolated, libart-compiler, libdexfile, libprofile, libartbase, libbacktrace, libartbase-art-gtest
+		"libart_headers",                     // depends on unconverted modules: art_libartbase_headers
+		"libartd",                            // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, libstatssocket, heapprofd_client_api, art_operator_srcs, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libcpu_features, libdexfiled, libartpalette, libbacktrace, libnativebridge, libnativeloader, libsigchain, libunwindstack, libartbased, libprofiled, cpp-define-generator-asm-support
+		"libartd-runtime-gtest",              // depends on unconverted modules: libgtest_isolated, libartd-compiler, libdexfiled, libprofiled, libartbased, libbacktrace, libartbased-art-gtest
+		"libstatslog_art",                    // depends on unconverted modules: statslog_art.cpp, statslog_art.h
+		"statslog_art.h", "statslog_art.cpp", // depends on unconverted modules: stats-log-api-gen
 
 		"libandroid_runtime_lazy", // depends on unconverted modules: libbinder_headers
 		"libcmd",                  // depends on unconverted modules: libbinder
 
+		"libdexfile_support_static",                                                       // Depends on unconverted module: libdexfile_external_headers
+		"libunwindstack_local", "libunwindstack_utils", "libc_malloc_debug", "libfdtrack", // Depends on unconverted module: libunwindstack
+
+		"libdexfile_support",          // TODO(b/210546943): Enabled based on product variables.
+		"libdexfile_external_headers", // TODO(b/210546943): Enabled based on product variables.
+
+		"libunwindstack",                // Depends on unconverted module libdexfile_support.
+		"libnativehelper_compat_libc++", // Broken compile: implicit declaration of function 'strerror_r' is invalid in C99
+
 		"chkcon", "sefcontext_compile", // depends on unconverted modules: libsepol
 
 		"libsepol", // TODO(b/207408632): Unsupported case of .l sources in cc library rules
 
-		"get_clang_version_test", // depends on unconverted module: get_clang_version
+		"gen-kotlin-build-file.py", // module has same name as source
 
-		"libbinder",               // TODO(b/188503688): Disabled for some archs,
 		"libactivitymanager_aidl", // TODO(b/207426160): Depends on activity_manager_procstate_aidl, which is an aidl filegroup.
 
-		"libnativehelper_lazy_mts_jni", // depends on unconverted modules: libgmock_ndk
-		"libnativehelper_mts_jni",      // depends on unconverted modules: libgmock_ndk
-		"libnativetesthelper_jni",      // depends on unconverted modules: libgtest_ndk_c++
+		"libnativehelper_lazy_mts_jni", "libnativehelper_mts_jni", // depends on unconverted modules: libgmock_ndk
+		"libnativetesthelper_jni", "libgmock_main_ndk", "libgmock_ndk", // depends on unconverted module: libgtest_ndk_c++
 
 		"statslog-framework-java-gen", "statslog.cpp", "statslog.h", "statslog.rs", "statslog_header.rs", // depends on unconverted modules: stats-log-api-gen
 
@@ -322,40 +420,47 @@
 
 		"libstatslog", // depends on unconverted modules: statslog.cpp, statslog.h, ...
 
-		"libgmock_main_ndk", "libgmock_ndk", // depends on unconverted module: libgtest_ndk_c++
-
 		"cmd",                                                        // depends on unconverted module packagemanager_aidl-cpp, of unsupported type aidl_interface
 		"servicedispatcher",                                          // depends on unconverted module android.debug_aidl, of unsupported type aidl_interface
 		"libutilscallstack",                                          // depends on unconverted module libbacktrace
 		"libbacktrace",                                               // depends on unconverted module libunwindstack
 		"libdebuggerd_handler",                                       // depends on unconverted module libdebuggerd_handler_core
 		"libdebuggerd_handler_core", "libdebuggerd_handler_fallback", // depends on unconverted module libdebuggerd
-		"unwind_for_offline",        // depends on unconverted module libunwindstack_utils
-		"libdebuggerd",              // depends on unconverted modules libdexfile_support, libunwindstack, gwp_asan_crash_handler, libtombstone_proto, libprotobuf-cpp-lite
-		"libdexfile_static",         // depends on libartpalette, libartbase, libdexfile, which are of unsupported type: art_cc_library.
-		"host_bionic_linker_asm",    // depends on extract_linker, a go binary.
-		"host_bionic_linker_script", // depends on extract_linker, a go binary.
+		"unwind_for_offline", // depends on unconverted module libunwindstack_utils
+		"libdebuggerd",       // depends on unconverted modules libdexfile_support, libunwindstack, gwp_asan_crash_handler, libtombstone_proto, libprotobuf-cpp-lite
+		"libdexfile_static",  // depends on libartpalette, libartbase, libdexfile, which are of unsupported type: art_cc_library.
 
-		"pbtombstone",                                  // depends on libprotobuf-cpp-lite, libtombstone_proto
-		"crash_dump",                                   // depends on unconverted module libprotobuf-cpp-lite
-		"libprotobuf-cpp-full", "libprotobuf-cpp-lite", // Unsupported product&vendor suffix. b/204811222 and b/204810610.
+		"static_crasher", // depends on unconverted modules: libdebuggerd_handler
 
-		"libunwindstack_local", "libunwindstack_utils", // depends on unconverted module libunwindstack
-		"libunwindstack",    // depends on libdexfile_support, of unsupported module type art_cc_library_static
-		"libc_malloc_debug", // depends on unconverted module libunwindstack
+		"pbtombstone", "crash_dump", // depends on libdebuggerd, libunwindstack
 
 		"libbase_ndk", // http://b/186826477, fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
 
-		"lib_linker_config_proto_lite", // contains .proto sources
+		"libprotobuf-internal-protos",      // b/210751803, we don't handle path property for filegroups
+		"libprotobuf-internal-python-srcs", // b/210751803, we don't handle path property for filegroups
+		"libprotobuf-java-full",            // b/210751803, we don't handle path property for filegroups
+		"host-libprotobuf-java-full",       // b/210751803, we don't handle path property for filegroups
+		"libprotobuf-java-util-full",       // b/210751803, we don't handle path property for filegroups
 
-		"libprotobuf-python",               // contains .proto sources
-		"libprotobuf-internal-protos",      // we don't handle path property for fileegroups
-		"libprotobuf-internal-python-srcs", // we don't handle path property for fileegroups
+		"conscrypt",          // b/210751803, we don't handle path property for filegroups
+		"conscrypt-for-host", // b/210751803, we don't handle path property for filegroups
 
-		"libseccomp_policy", // b/201094425: depends on func_to_syscall_nrs, which depends on py_binary, which is unsupported in mixed builds.
-		"libfdtrack",        // depends on unconverted module libunwindstack
+		"host-libprotobuf-java-lite",  // b/217236083, java_library cannot have deps without srcs
+		"host-libprotobuf-java-micro", // b/217236083, java_library cannot have deps without srcs
+		"host-libprotobuf-java-nano",  // b/217236083, java_library cannot have deps without srcs
+		"error_prone_core",            // b/217236083, java_library cannot have deps without srcs
+		"bouncycastle-host",           // b/217236083, java_library cannot have deps without srcs
 
-		"gwp_asan_crash_handler", // cc_library, ld.lld: error: undefined symbol: memset
+		"apex_manifest_proto_java", // b/215230097, we don't handle .proto files in java_library srcs attribute
+
+		"libc_musl_sysroot_bionic_arch_headers", // b/218405924, depends on soong_zip
+		"libc_musl_sysroot_bionic_headers",      // b/218405924, depends on soong_zip and generates duplicate srcs
+
+		// python protos
+		"libprotobuf-python",                           // contains .proto sources
+		"conv_linker_config",                           // depends on linker_config_proto, a python lib with proto sources
+		"apex_build_info_proto", "apex_manifest_proto", // a python lib with proto sources
+		"linker_config_proto", // contains .proto sources
 
 		"brotli-fuzzer-corpus", // b/202015218: outputs are in location incompatible with bazel genrule handling.
 
@@ -364,35 +469,16 @@
 		"platform_tools_properties",
 		"build_tools_source_properties",
 
-		// Tests. Handle later.
-		"libbionic_tests_headers_posix", // http://b/186024507, cc_library_static, sched.h, time.h not found
-		"libjemalloc5_integrationtest",
-		"libjemalloc5_stresstestlib",
-		"libjemalloc5_unittest",
-
 		// APEX support
-		"com.android.runtime", // http://b/194746715, apex, depends on 'libc_malloc_debug'
-
-		"libadb_crypto",                    // Depends on libadb_protos
-		"libadb_crypto_static",             // Depends on libadb_protos_static
-		"libadb_pairing_connection",        // Depends on libadb_protos
-		"libadb_pairing_connection_static", // Depends on libadb_protos_static
-		"libadb_pairing_server",            // Depends on libadb_protos
-		"libadb_pairing_server_static",     // Depends on libadb_protos_static
-		"libadbd",                          // Depends on libadbd_core
-		"libadbd_core",                     // Depends on libadb_protos
-		"libadbd_services",                 // Depends on libadb_protos
-
-		"libadb_protos_static",         // b/200601772: Requires cc_library proto support
-		"libadb_protos",                // b/200601772: Requires cc_library proto support
-		"libapp_processes_protos_lite", // b/200601772: Requires cc_library proto support
+		"com.android.runtime", // depends on unconverted modules: bionic-linker-config, linkerconfig
 
 		"libgtest_ndk_c++",      // b/201816222: Requires sdk_version support.
 		"libgtest_main_ndk_c++", // b/201816222: Requires sdk_version support.
 
-		"abb",                     // depends on unconverted modules: libadbd_core, libadbd_services,
-		"adb",                     // depends on unconverted modules: bin2c_fastdeployagent, libadb_crypto, libadb_host, libadb_pairing_connection, libadb_protos, libandroidfw, libapp_processes_protos_full, libfastdeploy_host, libopenscreen-discovery, libopenscreen-platform-impl, libusb, libzstd, AdbWinApi
-		"adbd",                    // depends on unconverted modules: libadb_crypto, libadb_pairing_connection, libadb_protos, libadbd, libadbd_core, libapp_processes_protos_lite, libzstd, libadbd_services, libcap, libminijail
+		"abb",                     // depends on unconverted modules: libcmd, libbinder
+		"adb",                     // depends on unconverted modules: AdbWinApi, libadb_host, libandroidfw, libapp_processes_protos_full, libfastdeploy_host, libopenscreen-discovery, libopenscreen-platform-impl, libusb, bin2c_fastdeployagent, AdbWinUsbApi
+		"libadb_host",             // depends on unconverted modules: libopenscreen-discovery, libopenscreen-platform-impl, libusb, AdbWinApi
+		"libfastdeploy_host",      // depends on unconverted modules: libandroidfw, libusb, AdbWinApi
 		"linker",                  // depends on unconverted modules: libdebuggerd_handler_fallback
 		"linker_reloc_bench_main", // depends on unconverted modules: liblinker_reloc_bench_*
 		"versioner",               // depends on unconverted modules: libclang_cxx_host, libLLVM_host, of unsupported type llvm_host_prebuilt_library_shared
@@ -400,24 +486,85 @@
 		"linkerconfig", // http://b/202876379 has arch-variant static_executable
 		"mdnsd",        // http://b/202876379 has arch-variant static_executable
 
-		"acvp_modulewrapper", // disabled for android x86/x86_64
+		"CarHTMLViewer", // depends on unconverted modules android.car-stubs, car-ui-lib
+
+		"libdexfile",  // depends on unconverted modules: dexfile_operator_srcs, libartbase, libartpalette,
+		"libdexfiled", // depends on unconverted modules: dexfile_operator_srcs, libartbased, libartpalette
+
+		// go deps:
+		"apex-protos",                                                                                // depends on soong_zip, a go binary
+		"generated_android_icu4j_src_files", "generated_android_icu4j_test_files", "icu4c_test_data", // depends on unconverted modules: soong_zip
+		"host_bionic_linker_asm",         // depends on extract_linker, a go binary.
+		"host_bionic_linker_script",      // depends on extract_linker, a go binary.
+		"robolectric-sqlite4java-native", // depends on soong_zip, a go binary
+		"robolectric_tzdata",             // depends on soong_zip, a go binary
+
+		"android_icu4j_srcgen_binary", // Bazel build error: deps not allowed without srcs; move to runtime_deps
+		"core-icu4j-for-host",         // Bazel build error: deps not allowed without srcs; move to runtime_deps
+
+		// java deps
+		"android_icu4j_srcgen",          // depends on unconverted modules: currysrc
+		"bin2c_fastdeployagent",         // depends on deployagent, a java binary
+		"currysrc",                      // depends on unconverted modules: currysrc_org.eclipse, guavalib, jopt-simple-4.9
+		"robolectric-sqlite4java-0.282", // depends on unconverted modules: robolectric-sqlite4java-import, robolectric-sqlite4java-native
+		"timezone-host",                 // depends on unconverted modules: art.module.api.annotations
+		"truth-host-prebuilt",           // depends on unconverted modules: truth-prebuilt
+		"truth-prebuilt",                // depends on unconverted modules: asm-7.0, guava
+
+		"generated_android_icu4j_resources",      // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
+		"generated_android_icu4j_test_resources", // depends on unconverted modules: android_icu4j_srcgen_binary, soong_zip
+
+		"art-script",     // depends on unconverted modules: dalvikvm, dex2oat
+		"dex2oat-script", // depends on unconverted modules: dex2oat
+
+		"error_prone_checkerframework_dataflow_nullaway", // TODO(b/219908977): "Error in fail: deps not allowed without srcs; move to runtime_deps?"
 	}
 
 	// 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{
-		"libjemalloc5", // http://b/188503688, cc_library, `target: { android: { enabled: false } }` for android targets.
-	}
+	bp2buildCcLibraryStaticOnlyList = []string{}
 
 	// Per-module denylist to opt modules out of mixed builds. Such modules will
 	// still be generated via bp2build.
 	mixedBuildsDisabledList = []string{
+		"art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found
+
 		"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
 		"minijail_constants_json", // http://b/200899432, bazel-built cc_genrule does not work in mixed build when it is a dependency of another soong module.
 
 		"cap_names.h",                                  // TODO(b/204913827) runfiles need to be handled in mixed builds
 		"libcap",                                       // TODO(b/204913827) runfiles need to be handled in mixed builds
 		"libprotobuf-cpp-full", "libprotobuf-cpp-lite", // Unsupported product&vendor suffix. b/204811222 and b/204810610.
+
+		// Depends on libprotobuf-cpp-*
+		"libadb_pairing_connection",
+		"libadb_pairing_connection_static",
+		"libadb_pairing_server", "libadb_pairing_server_static",
+
+		// TODO(b/204811222) support suffix in cc_binary
+		"acvp_modulewrapper",
+		"android.hardware.media.c2@1.0-service-v4l2",
+		"app_process",
+		"bar_test",
+		"bench_cxa_atexit",
+		"bench_noop",
+		"bench_noop_nostl",
+		"bench_noop_static",
+		"boringssl_self_test",
+		"boringssl_self_test_vendor",
+		"bssl",
+		"cavp",
+		"crash_dump",
+		"crasher",
+		"libcxx_test_template",
+		"linker",
+		"memory_replay",
+		"native_bridge_guest_linker",
+		"native_bridge_stub_library_defaults",
+		"noop",
+		"simpleperf_ndk",
+		"toybox-static",
+		"zlib_bench",
 	}
 
 	// Used for quicker lookups
@@ -468,6 +615,9 @@
 		// Windows toolchains are not currently supported.
 		return false
 	}
+	if !ctx.Module().Enabled() {
+		return false
+	}
 	if !ctx.Config().BazelContext.BazelEnabled() {
 		return false
 	}
@@ -490,32 +640,21 @@
 	if !ok {
 		return false
 	}
-	return b.convertWithBp2build(ctx, module) || b.HasHandcraftedLabel()
+	return b.shouldConvertWithBp2build(ctx, module) || b.HasHandcraftedLabel()
 }
 
-// ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
-func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionContext) bool {
-	return b.convertWithBp2build(ctx, ctx.Module())
+// ShouldConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
+func (b *BazelModuleBase) ShouldConvertWithBp2build(ctx BazelConversionContext) bool {
+	return b.shouldConvertWithBp2build(ctx, ctx.Module())
 }
 
-func (b *BazelModuleBase) convertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool {
+func (b *BazelModuleBase) shouldConvertWithBp2build(ctx BazelConversionContext, 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.OtherModuleType(module)] == false {
-		if b, ok := module.(Bazelable); ok && b.BaseModuleType() != "" {
-			// For modules with custom types from soong_config_module_types,
-			// check that their _base module type_ has a bp2build mutator.
-			if ctx.Config().bp2buildModuleTypeConfig[b.BaseModuleType()] == false {
-				return false
-			}
-		} else {
-			return false
-		}
+	if !b.bazelProps().Bazel_module.CanConvertToBazel {
+		return false
 	}
 
 	packagePath := ctx.OtherModuleDir(module)
@@ -589,3 +728,36 @@
 	}
 	return string(data[:]), nil
 }
+
+func registerBp2buildConversionMutator(ctx RegisterMutatorsContext) {
+	ctx.TopDown("bp2build_conversion", convertWithBp2build).Parallel()
+}
+
+func convertWithBp2build(ctx TopDownMutatorContext) {
+	bModule, ok := ctx.Module().(Bazelable)
+	if !ok || !bModule.shouldConvertWithBp2build(ctx, ctx.Module()) {
+		return
+	}
+
+	bModule.ConvertWithBp2build(ctx)
+}
+
+// GetMainClassInManifest scans the manifest file specified in filepath and returns
+// the value of attribute Main-Class in the manifest file if it exists, or returns error.
+// WARNING: this is for bp2build converters of java_* modules only.
+func GetMainClassInManifest(c Config, filepath string) (string, error) {
+	file, err := c.fs.Open(filepath)
+	if err != nil {
+		return "", err
+	}
+	defer file.Close()
+	scanner := bufio.NewScanner(file)
+	for scanner.Scan() {
+		line := scanner.Text()
+		if strings.HasPrefix(line, "Main-Class:") {
+			return strings.TrimSpace(line[len("Main-Class:"):]), nil
+		}
+	}
+
+	return "", errors.New("Main-Class is not found.")
+}
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 729c73c..f353a9d 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -92,6 +92,7 @@
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
 	ModuleFromName(name string) (blueprint.Module, bool)
 	AddUnconvertedBp2buildDep(string)
+	AddMissingBp2buildDep(dep string)
 }
 
 // BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
@@ -129,8 +130,10 @@
 		}
 		if m, t := SrcIsModuleWithTag(module); m != "" {
 			l := getOtherModuleLabel(ctx, m, t, moduleToLabelFn)
-			l.OriginalModuleName = bpText
-			labels.Includes = append(labels.Includes, l)
+			if l != nil {
+				l.OriginalModuleName = bpText
+				labels.Includes = append(labels.Includes, *l)
+			}
 		} else {
 			ctx.ModuleErrorf("%q, is not a module reference", module)
 		}
@@ -157,11 +160,17 @@
 }
 
 func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
-	return BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes[0]
+	if srcs := BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
+		return srcs[0]
+	}
+	return bazel.Label{}
 }
 
 func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label {
-	return BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes[0]
+	if srcs := BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
+		return srcs[0]
+	}
+	return bazel.Label{}
 }
 
 // BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module
@@ -279,6 +288,16 @@
 	return newPaths
 }
 
+// Converts root-relative Paths to a list of bazel.Label relative to the module in ctx.
+func RootToModuleRelativePaths(ctx BazelConversionPathContext, paths Paths) []bazel.Label {
+	var newPaths []bazel.Label
+	for _, path := range PathsWithModuleSrcSubDir(ctx, paths, "") {
+		s := path.Rel()
+		newPaths = append(newPaths, bazel.Label{Label: s})
+	}
+	return newPaths
+}
+
 // expandSrcsForBazel returns bazel.LabelList with paths rooted from the module's local source
 // directory and Bazel target labels, excluding those included in the excludes argument (which
 // should already be expanded to resolve references to Soong-modules). Valid elements of paths
@@ -318,9 +337,9 @@
 	for _, p := range paths {
 		if m, tag := SrcIsModuleWithTag(p); m != "" {
 			l := getOtherModuleLabel(ctx, m, tag, BazelModuleLabel)
-			if !InList(l.Label, expandedExcludes) {
+			if l != nil && !InList(l.Label, expandedExcludes) {
 				l.OriginalModuleName = fmt.Sprintf(":%s", m)
-				labels.Includes = append(labels.Includes, l)
+				labels.Includes = append(labels.Includes, *l)
 			}
 		} else {
 			var expandedPaths []bazel.Label
@@ -328,12 +347,7 @@
 				// e.g. turn "math/*.c" in
 				// external/arm-optimized-routines to external/arm-optimized-routines/math/*.c
 				rootRelativeGlobPath := pathForModuleSrc(ctx, p).String()
-				globbedPaths := GlobFiles(ctx, rootRelativeGlobPath, rootRelativeExpandedExcludes)
-				globbedPaths = PathsWithModuleSrcSubDir(ctx, globbedPaths, "")
-				for _, path := range globbedPaths {
-					s := path.Rel()
-					expandedPaths = append(expandedPaths, bazel.Label{Label: s})
-				}
+				expandedPaths = RootToModuleRelativePaths(ctx, GlobFiles(ctx, rootRelativeGlobPath, rootRelativeExpandedExcludes))
 			} else {
 				if !InList(p, expandedExcludes) {
 					expandedPaths = append(expandedPaths, bazel.Label{Label: p})
@@ -349,10 +363,16 @@
 // 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,
-	labelFromModule func(BazelConversionPathContext, blueprint.Module) string) bazel.Label {
+	labelFromModule func(BazelConversionPathContext, blueprint.Module) string) *bazel.Label {
 	m, _ := ctx.ModuleFromName(dep)
+	// The module was not found in an Android.bp file, this is often due to:
+	//		* a limited manifest
+	//		* a required module not being converted from Android.mk
 	if m == nil {
-		panic(fmt.Errorf("No module named %q found, but was a direct dep of %q", dep, ctx.Module().Name()))
+		ctx.AddMissingBp2buildDep(dep)
+		return &bazel.Label{
+			Label: ":" + dep + "__BP2BUILD__MISSING__DEP",
+		}
 	}
 	if !convertedToBazel(ctx, m) {
 		ctx.AddUnconvertedBp2buildDep(dep)
@@ -366,7 +386,7 @@
 		otherLabel = bazelShortLabel(otherLabel)
 	}
 
-	return bazel.Label{
+	return &bazel.Label{
 		Label: otherLabel,
 	}
 }
diff --git a/android/config.go b/android/config.go
index ddad1f5..57eff92 100644
--- a/android/config.go
+++ b/android/config.go
@@ -24,6 +24,7 @@
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"reflect"
 	"runtime"
 	"strconv"
 	"strings"
@@ -37,6 +38,7 @@
 	"android/soong/android/soongconfig"
 	"android/soong/bazel"
 	"android/soong/remoteexec"
+	"android/soong/starlark_fmt"
 )
 
 // Bool re-exports proptools.Bool for the android package.
@@ -157,7 +159,6 @@
 
 	runningAsBp2Build              bool
 	bp2buildPackageConfig          Bp2BuildConfig
-	bp2buildModuleTypeConfig       map[string]bool
 	Bp2buildSoongConfigDefinitions soongconfig.Bp2BuildSoongConfigDefinitions
 
 	// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
@@ -274,15 +275,43 @@
 		return fmt.Errorf("Could not create dir %s: %s", dir, err)
 	}
 
-	data, err := json.MarshalIndent(&config, "", "    ")
+	nonArchVariantProductVariables := []string{}
+	archVariantProductVariables := []string{}
+	p := variableProperties{}
+	t := reflect.TypeOf(p.Product_variables)
+	for i := 0; i < t.NumField(); i++ {
+		f := t.Field(i)
+		nonArchVariantProductVariables = append(nonArchVariantProductVariables, strings.ToLower(f.Name))
+		if proptools.HasTag(f, "android", "arch_variant") {
+			archVariantProductVariables = append(archVariantProductVariables, strings.ToLower(f.Name))
+		}
+	}
+
+	nonArchVariantProductVariablesJson := starlark_fmt.PrintStringList(nonArchVariantProductVariables, 0)
+	if err != nil {
+		return fmt.Errorf("cannot marshal product variable data: %s", err.Error())
+	}
+
+	archVariantProductVariablesJson := starlark_fmt.PrintStringList(archVariantProductVariables, 0)
+	if err != nil {
+		return fmt.Errorf("cannot marshal arch variant product variable data: %s", err.Error())
+	}
+
+	configJson, err := json.MarshalIndent(&config, "", "    ")
 	if err != nil {
 		return fmt.Errorf("cannot marshal config data: %s", err.Error())
 	}
 
 	bzl := []string{
 		bazel.GeneratedBazelFileWarning,
-		fmt.Sprintf(`_product_vars = json.decode("""%s""")`, data),
-		"product_vars = _product_vars\n",
+		fmt.Sprintf(`_product_vars = json.decode("""%s""")`, configJson),
+		fmt.Sprintf(`_product_var_constraints = %s`, nonArchVariantProductVariablesJson),
+		fmt.Sprintf(`_arch_variant_product_var_constraints = %s`, archVariantProductVariablesJson),
+		"\n", `
+product_vars = _product_vars
+product_var_constraints = _product_var_constraints
+arch_variant_product_var_constraints = _arch_variant_product_var_constraints
+`,
 	}
 	err = ioutil.WriteFile(filepath.Join(dir, "product_variables.bzl"), []byte(strings.Join(bzl, "\n")), 0644)
 	if err != nil {
@@ -353,8 +382,6 @@
 
 	config.mockFileSystem(bp, fs)
 
-	config.bp2buildModuleTypeConfig = map[string]bool{}
-
 	determineBuildOS(config)
 
 	return Config{config}
@@ -522,7 +549,6 @@
 
 	config.BazelContext, err = NewBazelContext(config)
 	config.bp2buildPackageConfig = bp2buildDefaultConfig
-	config.bp2buildModuleTypeConfig = make(map[string]bool)
 
 	return Config{config}, err
 }
@@ -573,9 +599,6 @@
 
 func (c *config) HostToolPath(ctx PathContext, tool string) Path {
 	path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin", false, tool)
-	if ctx.Config().KatiEnabled() {
-		path = path.ToMakePath()
-	}
 	return path
 }
 
@@ -585,17 +608,11 @@
 		ext = ".dylib"
 	}
 	path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "lib64", false, lib+ext)
-	if ctx.Config().KatiEnabled() {
-		path = path.ToMakePath()
-	}
 	return path
 }
 
 func (c *config) HostJavaToolPath(ctx PathContext, tool string) Path {
 	path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "framework", false, tool)
-	if ctx.Config().KatiEnabled() {
-		path = path.ToMakePath()
-	}
 	return path
 }
 
@@ -892,8 +909,13 @@
 	return Bool(c.productVariables.Eng)
 }
 
+// DevicePrimaryArchType returns the ArchType for the first configured device architecture, or
+// Common if there are no device architectures.
 func (c *config) DevicePrimaryArchType() ArchType {
-	return c.Targets[Android][0].Arch.ArchType
+	if androidTargets := c.Targets[Android]; len(androidTargets) > 0 {
+		return androidTargets[0].Arch.ArchType
+	}
+	return Common
 }
 
 func (c *config) SanitizeHost() []string {
@@ -1194,10 +1216,6 @@
 	return c.config.productVariables.DeviceKernelHeaders
 }
 
-func (c *deviceConfig) SamplingPGO() bool {
-	return Bool(c.config.productVariables.SamplingPGO)
-}
-
 // JavaCoverageEnabledForPath returns whether Java code coverage is enabled for
 // path. Coverage is enabled by default when the product variable
 // JavaCoveragePaths is empty. If JavaCoveragePaths is not empty, coverage is
@@ -1253,6 +1271,10 @@
 	return coverage
 }
 
+func (c *deviceConfig) AfdoAdditionalProfileDirs() []string {
+	return c.config.productVariables.AfdoAdditionalProfileDirs
+}
+
 func (c *deviceConfig) PgoAdditionalProfileDirs() []string {
 	return c.config.productVariables.PgoAdditionalProfileDirs
 }
@@ -1473,6 +1495,10 @@
 	return String(c.config.productVariables.TotSepolicyVersion)
 }
 
+func (c *deviceConfig) PlatformSepolicyCompatVersions() []string {
+	return c.config.productVariables.PlatformSepolicyCompatVersions
+}
+
 func (c *deviceConfig) BoardSepolicyVers() string {
 	if ver := String(c.config.productVariables.BoardSepolicyVers); ver != "" {
 		return ver
@@ -1480,10 +1506,42 @@
 	return c.PlatformSepolicyVersion()
 }
 
+func (c *deviceConfig) BoardPlatVendorPolicy() []string {
+	return c.config.productVariables.BoardPlatVendorPolicy
+}
+
 func (c *deviceConfig) BoardReqdMaskPolicy() []string {
 	return c.config.productVariables.BoardReqdMaskPolicy
 }
 
+func (c *deviceConfig) BoardSystemExtPublicPrebuiltDirs() []string {
+	return c.config.productVariables.BoardSystemExtPublicPrebuiltDirs
+}
+
+func (c *deviceConfig) BoardSystemExtPrivatePrebuiltDirs() []string {
+	return c.config.productVariables.BoardSystemExtPrivatePrebuiltDirs
+}
+
+func (c *deviceConfig) BoardProductPublicPrebuiltDirs() []string {
+	return c.config.productVariables.BoardProductPublicPrebuiltDirs
+}
+
+func (c *deviceConfig) BoardProductPrivatePrebuiltDirs() []string {
+	return c.config.productVariables.BoardProductPrivatePrebuiltDirs
+}
+
+func (c *deviceConfig) SystemExtSepolicyPrebuiltApiDir() string {
+	return String(c.config.productVariables.SystemExtSepolicyPrebuiltApiDir)
+}
+
+func (c *deviceConfig) ProductSepolicyPrebuiltApiDir() string {
+	return String(c.config.productVariables.ProductSepolicyPrebuiltApiDir)
+}
+
+func (c *deviceConfig) IsPartnerTrebleSepolicyTestEnabled() bool {
+	return c.SystemExtSepolicyPrebuiltApiDir() != "" || c.ProductSepolicyPrebuiltApiDir() != ""
+}
+
 func (c *deviceConfig) DirectedVendorSnapshot() bool {
 	return c.config.productVariables.DirectedVendorSnapshot
 }
@@ -1697,7 +1755,7 @@
 }
 
 // Append a list of (apex, jar) pairs to the list.
-func (l *ConfiguredJarList) AppendList(other ConfiguredJarList) ConfiguredJarList {
+func (l *ConfiguredJarList) AppendList(other *ConfiguredJarList) ConfiguredJarList {
 	apexes := make([]string, 0, l.Len()+other.Len())
 	jars := make([]string, 0, l.Len()+other.Len())
 
@@ -1936,3 +1994,8 @@
 func (c *config) RBEWrapper() string {
 	return c.GetenvWithDefault("RBE_WRAPPER", remoteexec.DefaultWrapperPath)
 }
+
+// UseHostMusl returns true if the host target has been configured to build against musl libc.
+func (c *config) UseHostMusl() bool {
+	return Bool(c.productVariables.HostMusl)
+}
diff --git a/android/deapexer.go b/android/deapexer.go
index bed6574..265f531 100644
--- a/android/deapexer.go
+++ b/android/deapexer.go
@@ -143,12 +143,16 @@
 }
 
 // FindDeapexerProviderForModule searches through the direct dependencies of the current context
-// module for a DeapexerTag dependency and returns its DeapexerInfo. If there is an error then it is
-// reported with ctx.ModuleErrorf and nil is returned.
+// module for a DeapexerTag dependency and returns its DeapexerInfo. If a single nonambiguous
+// deapexer module isn't found then errors are reported with ctx.ModuleErrorf and nil is returned.
 func FindDeapexerProviderForModule(ctx ModuleContext) *DeapexerInfo {
 	var di *DeapexerInfo
 	ctx.VisitDirectDepsWithTag(DeapexerTag, func(m Module) {
 		p := ctx.OtherModuleProvider(m, DeapexerProvider).(DeapexerInfo)
+		if di != nil {
+			ctx.ModuleErrorf("Multiple installable prebuilt APEXes provide ambiguous deapexers: %s and %s",
+				di.ApexModuleName(), p.ApexModuleName())
+		}
 		di = &p
 	})
 	if di != nil {
diff --git a/android/defaults.go b/android/defaults.go
index 9046002..8b121f6 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -89,14 +89,13 @@
 var _ Defaultable = (*DefaultableModuleBase)(nil)
 
 func InitDefaultableModule(module DefaultableModule) {
-	if module.(Module).base().module == nil {
+	if module.base().module == nil {
 		panic("InitAndroidModule must be called before InitDefaultableModule")
 	}
-	module.setProperties(module.(Module).GetProperties(), module.(Module).base().variableProperties)
+
+	module.setProperties(module.GetProperties(), module.base().variableProperties)
 
 	module.AddProperties(module.defaults())
-
-	module.base().customizableProperties = module.GetProperties()
 }
 
 // A restricted subset of context methods, similar to LoadHookContext.
@@ -118,6 +117,11 @@
 
 type DefaultsModuleBase struct {
 	DefaultableModuleBase
+
+	// Included to support setting bazel_module.label for multiple Soong modules to the same Bazel
+	// target. This is primarily useful for modules that were architecture specific and instead are
+	// handled in Bazel as a select().
+	BazelModuleBase
 }
 
 // The common pattern for defaults modules is to register separate instances of
@@ -160,6 +164,7 @@
 type DefaultsModule interface {
 	Module
 	Defaults
+	Bazelable
 }
 
 func (d *DefaultsModuleBase) properties() []interface{} {
@@ -170,8 +175,11 @@
 	return d.defaultableVariableProperties
 }
 
-func (d *DefaultsModuleBase) GenerateAndroidBuildActions(ctx ModuleContext) {
-}
+func (d *DefaultsModuleBase) GenerateAndroidBuildActions(ctx ModuleContext) {}
+
+// ConvertWithBp2build to fulfill Bazelable interface; however, at this time defaults module are
+// *NOT* converted with bp2build
+func (defaultable *DefaultsModuleBase) ConvertWithBp2build(ctx TopDownMutatorContext) {}
 
 func InitDefaultsModule(module DefaultsModule) {
 	commonProperties := &commonProperties{}
@@ -182,6 +190,8 @@
 		&ApexProperties{},
 		&distProperties{})
 
+	// Bazel module must be initialized _before_ Defaults to be included in cc_defaults module.
+	InitBazelModule(module)
 	initAndroidModuleBase(module)
 	initProductVariableModule(module)
 	initArchModule(module)
@@ -208,7 +218,6 @@
 	// The applicable licenses property for defaults is 'licenses'.
 	setPrimaryLicensesProperty(module, "licenses", &commonProperties.Licenses)
 
-	base.module = module
 }
 
 var _ Defaults = (*DefaultsModuleBase)(nil)
diff --git a/android/filegroup.go b/android/filegroup.go
index a79374d..c932ffa 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -22,7 +22,6 @@
 
 func init() {
 	RegisterModuleType("filegroup", FileGroupFactory)
-	RegisterBp2BuildMutator("filegroup", FilegroupBp2Build)
 }
 
 var PrepareForTestWithFilegroup = FixtureRegisterWithContext(func(ctx RegistrationContext) {
@@ -34,12 +33,8 @@
 	Srcs bazel.LabelListAttribute
 }
 
-func FilegroupBp2Build(ctx TopDownMutatorContext) {
-	fg, ok := ctx.Module().(*fileGroup)
-	if !ok || !fg.ConvertWithBp2build(ctx) {
-		return
-	}
-
+// ConvertWithBp2build performs bp2build conversion of filegroup
+func (fg *fileGroup) ConvertWithBp2build(ctx TopDownMutatorContext) {
 	srcs := bazel.MakeLabelListAttribute(
 		BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs))
 
diff --git a/android/hooks.go b/android/hooks.go
index 9eaa1ac..bded764 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -68,7 +68,7 @@
 func (l *loadHookContext) appendPrependHelper(props []interface{},
 	extendFn func([]interface{}, interface{}, proptools.ExtendPropertyFilterFunc) error) {
 	for _, p := range props {
-		err := extendFn(l.Module().base().customizableProperties, p, nil)
+		err := extendFn(l.Module().base().GetProperties(), p, nil)
 		if err != nil {
 			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
 				l.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
diff --git a/android/license.go b/android/license.go
index 8bfd3ba..ebee055 100644
--- a/android/license.go
+++ b/android/license.go
@@ -63,7 +63,7 @@
 func (m *licenseModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 	// license modules have no licenses, but license_kinds must refer to license_kind modules
 	mergeStringProps(&m.base().commonProperties.Effective_licenses, ctx.ModuleName())
-	mergePathProps(&m.base().commonProperties.Effective_license_text, PathsForModuleSrc(ctx, m.properties.License_text)...)
+	namePathProps(&m.base().commonProperties.Effective_license_text, m.properties.Package_name, PathsForModuleSrc(ctx, m.properties.License_text)...)
 	for _, module := range ctx.GetDirectDepsWithTag(licenseKindTag) {
 		if lk, ok := module.(*licenseKindModule); ok {
 			mergeStringProps(&m.base().commonProperties.Effective_license_conditions, lk.properties.Conditions...)
@@ -80,9 +80,6 @@
 	base := module.base()
 	module.AddProperties(&base.nameProperties, &module.properties)
 
-	base.generalProperties = module.GetProperties()
-	base.customizableProperties = module.GetProperties()
-
 	// The visibility property needs to be checked and parsed by the visibility module.
 	setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
 
diff --git a/android/license_kind.go b/android/license_kind.go
index ddecd77..838dedd 100644
--- a/android/license_kind.go
+++ b/android/license_kind.go
@@ -53,9 +53,6 @@
 	base := module.base()
 	module.AddProperties(&base.nameProperties, &module.properties)
 
-	base.generalProperties = module.GetProperties()
-	base.customizableProperties = module.GetProperties()
-
 	// The visibility property needs to be checked and parsed by the visibility module.
 	setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
 
diff --git a/android/license_metadata.go b/android/license_metadata.go
new file mode 100644
index 0000000..6a5b0da
--- /dev/null
+++ b/android/license_metadata.go
@@ -0,0 +1,254 @@
+// 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 android
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+var (
+	_ = pctx.HostBinToolVariable("licenseMetadataCmd", "build_license_metadata")
+
+	licenseMetadataRule = pctx.AndroidStaticRule("licenseMetadataRule", blueprint.RuleParams{
+		Command:        "${licenseMetadataCmd} -o $out @${out}.rsp",
+		CommandDeps:    []string{"${licenseMetadataCmd}"},
+		Rspfile:        "${out}.rsp",
+		RspfileContent: "${args}",
+	}, "args")
+)
+
+func buildLicenseMetadata(ctx ModuleContext, licenseMetadataFile WritablePath) {
+	base := ctx.Module().base()
+
+	if !base.Enabled() {
+		return
+	}
+
+	if exemptFromRequiredApplicableLicensesProperty(ctx.Module()) {
+		return
+	}
+
+	var outputFiles Paths
+	if outputFileProducer, ok := ctx.Module().(OutputFileProducer); ok {
+		outputFiles, _ = outputFileProducer.OutputFiles("")
+		outputFiles = PathsIfNonNil(outputFiles...)
+	}
+
+	isContainer := isContainerFromFileExtensions(base.installFiles, outputFiles)
+
+	var allDepMetadataFiles Paths
+	var allDepMetadataArgs []string
+	var allDepOutputFiles Paths
+	var allDepMetadataDepSets []*PathsDepSet
+
+	ctx.VisitDirectDepsBlueprint(func(bpdep blueprint.Module) {
+		dep, _ := bpdep.(Module)
+		if dep == nil {
+			return
+		}
+		if !dep.Enabled() {
+			return
+		}
+
+		if ctx.OtherModuleHasProvider(dep, LicenseMetadataProvider) {
+			info := ctx.OtherModuleProvider(dep, LicenseMetadataProvider).(*LicenseMetadataInfo)
+			allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath)
+			if isContainer || IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) {
+				allDepMetadataDepSets = append(allDepMetadataDepSets, info.LicenseMetadataDepSet)
+			}
+
+			depAnnotations := licenseAnnotationsFromTag(ctx.OtherModuleDependencyTag(dep))
+
+			allDepMetadataArgs = append(allDepMetadataArgs, info.LicenseMetadataPath.String()+depAnnotations)
+
+			if depInstallFiles := dep.base().installFiles; len(depInstallFiles) > 0 {
+				allDepOutputFiles = append(allDepOutputFiles, depInstallFiles.Paths()...)
+			} else if depOutputFiles, err := outputFilesForModule(ctx, dep, ""); err == nil {
+				depOutputFiles = PathsIfNonNil(depOutputFiles...)
+				allDepOutputFiles = append(allDepOutputFiles, depOutputFiles...)
+			}
+		}
+	})
+
+	allDepMetadataFiles = SortedUniquePaths(allDepMetadataFiles)
+	sort.Strings(allDepMetadataArgs)
+	allDepOutputFiles = SortedUniquePaths(allDepOutputFiles)
+
+	var orderOnlyDeps Paths
+	var args []string
+
+	if t := ctx.ModuleType(); t != "" {
+		args = append(args,
+			"-mt "+proptools.NinjaAndShellEscape(t))
+	}
+
+	args = append(args,
+		"-r "+proptools.NinjaAndShellEscape(ctx.ModuleDir()),
+		"-mc UNKNOWN")
+
+	if p := base.commonProperties.Effective_package_name; p != nil {
+		args = append(args,
+			"-p "+proptools.NinjaAndShellEscape(*p))
+	}
+
+	args = append(args,
+		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_kinds), "-k "))
+
+	args = append(args,
+		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_conditions), "-c "))
+
+	args = append(args,
+		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.commonProperties.Effective_license_text.Strings()), "-n "))
+
+	if isContainer {
+		transitiveDeps := newPathsDepSet(nil, allDepMetadataDepSets).ToList()
+		args = append(args,
+			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(transitiveDeps.Strings()), "-d "))
+		orderOnlyDeps = append(orderOnlyDeps, transitiveDeps...)
+	} else {
+		args = append(args,
+			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(allDepMetadataArgs), "-d "))
+		orderOnlyDeps = append(orderOnlyDeps, allDepMetadataFiles...)
+	}
+
+	args = append(args,
+		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(allDepOutputFiles.Strings()), "-s "))
+
+	// Install map
+	args = append(args,
+		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.licenseInstallMap), "-m "))
+
+	// Built files
+	if len(outputFiles) > 0 {
+		args = append(args,
+			JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(outputFiles.Strings()), "-t "))
+	} else {
+		args = append(args, fmt.Sprintf("-t //%s:%s", ctx.ModuleDir(), ctx.ModuleName()))
+	}
+
+	// Installed files
+	args = append(args,
+		JoinWithPrefix(proptools.NinjaAndShellEscapeListIncludingSpaces(base.installFiles.Strings()), "-i "))
+
+	if isContainer {
+		args = append(args, "--is_container")
+	}
+
+	ctx.Build(pctx, BuildParams{
+		Rule:        licenseMetadataRule,
+		Output:      licenseMetadataFile,
+		OrderOnly:   orderOnlyDeps,
+		Description: "license metadata",
+		Args: map[string]string{
+			"args": strings.Join(args, " "),
+		},
+	})
+
+	ctx.SetProvider(LicenseMetadataProvider, &LicenseMetadataInfo{
+		LicenseMetadataPath:   licenseMetadataFile,
+		LicenseMetadataDepSet: newPathsDepSet(Paths{licenseMetadataFile}, allDepMetadataDepSets),
+	})
+}
+
+func isContainerFromFileExtensions(installPaths InstallPaths, builtPaths Paths) bool {
+	var paths Paths
+	if len(installPaths) > 0 {
+		paths = installPaths.Paths()
+	} else {
+		paths = builtPaths
+	}
+
+	for _, path := range paths {
+		switch path.Ext() {
+		case ".zip", ".tar", ".tgz", ".tar.gz", ".img", ".srcszip", ".apex":
+			return true
+		}
+	}
+
+	return false
+}
+
+// LicenseMetadataProvider is used to propagate license metadata paths between modules.
+var LicenseMetadataProvider = blueprint.NewProvider(&LicenseMetadataInfo{})
+
+// LicenseMetadataInfo stores the license metadata path for a module.
+type LicenseMetadataInfo struct {
+	LicenseMetadataPath   Path
+	LicenseMetadataDepSet *PathsDepSet
+}
+
+// licenseAnnotationsFromTag returns the LicenseAnnotations for a tag (if any) converted into
+// a string, or an empty string if there are none.
+func licenseAnnotationsFromTag(tag blueprint.DependencyTag) string {
+	if annoTag, ok := tag.(LicenseAnnotationsDependencyTag); ok {
+		annos := annoTag.LicenseAnnotations()
+		if len(annos) > 0 {
+			annoStrings := make([]string, len(annos))
+			for i, s := range annos {
+				annoStrings[i] = string(s)
+			}
+			return ":" + strings.Join(annoStrings, ",")
+		}
+	}
+	return ""
+}
+
+// LicenseAnnotationsDependencyTag is implemented by dependency tags in order to provide a
+// list of license dependency annotations.
+type LicenseAnnotationsDependencyTag interface {
+	LicenseAnnotations() []LicenseAnnotation
+}
+
+// LicenseAnnotation is an enum of annotations that can be applied to dependencies for propagating
+// license information.
+type LicenseAnnotation string
+
+const (
+	// LicenseAnnotationSharedDependency should be returned by LicenseAnnotations implementations
+	// of dependency tags when the usage of the dependency is dynamic, for example a shared library
+	// linkage for native modules or as a classpath library for java modules.
+	//
+	// Dependency tags that need to always return LicenseAnnotationSharedDependency
+	// can embed LicenseAnnotationSharedDependencyTag to implement LicenseAnnotations.
+	LicenseAnnotationSharedDependency LicenseAnnotation = "dynamic"
+
+	// LicenseAnnotationToolchain should be returned by LicenseAnnotations implementations of
+	// dependency tags when the dependency is used as a toolchain.
+	//
+	// Dependency tags that need to always return LicenseAnnotationToolchain
+	// can embed LicenseAnnotationToolchainDependencyTag to implement LicenseAnnotations.
+	LicenseAnnotationToolchain LicenseAnnotation = "toolchain"
+)
+
+// LicenseAnnotationSharedDependencyTag can be embedded in a dependency tag to implement
+// LicenseAnnotations that always returns LicenseAnnotationSharedDependency.
+type LicenseAnnotationSharedDependencyTag struct{}
+
+func (LicenseAnnotationSharedDependencyTag) LicenseAnnotations() []LicenseAnnotation {
+	return []LicenseAnnotation{LicenseAnnotationSharedDependency}
+}
+
+// LicenseAnnotationToolchainDependencyTag can be embedded in a dependency tag to implement
+// LicenseAnnotations that always returns LicenseAnnotationToolchain.
+type LicenseAnnotationToolchainDependencyTag struct{}
+
+func (LicenseAnnotationToolchainDependencyTag) LicenseAnnotations() []LicenseAnnotation {
+	return []LicenseAnnotation{LicenseAnnotationToolchain}
+}
diff --git a/android/license_sdk_member.go b/android/license_sdk_member.go
index 2ce921b..b17defe 100644
--- a/android/license_sdk_member.go
+++ b/android/license_sdk_member.go
@@ -90,7 +90,10 @@
 	// Populate the properties from the variant.
 	l := variant.(*licenseModule)
 	p.License_kinds = l.properties.License_kinds
-	p.License_text = l.base().commonProperties.Effective_license_text
+	p.License_text = make(Paths, 0, len(l.base().commonProperties.Effective_license_text))
+	for _, np := range l.base().commonProperties.Effective_license_text {
+		p.License_text = append(p.License_text, np.Path)
+	}
 }
 
 func (p *licenseSdkMemberProperties) AddToPropertySet(ctx SdkMemberContext, propertySet BpPropertySet) {
diff --git a/android/licenses.go b/android/licenses.go
index e9e271b..b51a06b 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -213,7 +213,7 @@
 				m.base().commonProperties.Effective_package_name = l.properties.Package_name
 			}
 			mergeStringProps(&m.base().commonProperties.Effective_licenses, module.base().commonProperties.Effective_licenses...)
-			mergePathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
+			mergeNamedPathProps(&m.base().commonProperties.Effective_license_text, module.base().commonProperties.Effective_license_text...)
 			mergeStringProps(&m.base().commonProperties.Effective_license_kinds, module.base().commonProperties.Effective_license_kinds...)
 			mergeStringProps(&m.base().commonProperties.Effective_license_conditions, module.base().commonProperties.Effective_license_conditions...)
 		} else {
@@ -239,10 +239,24 @@
 	*prop = SortedUniqueStrings(*prop)
 }
 
-// Update a property Path array with a distinct union of its values and a list of new values.
-func mergePathProps(prop *Paths, values ...Path) {
+// Update a property NamedPath array with a distinct union of its values and a list of new values.
+func namePathProps(prop *NamedPaths, name *string, values ...Path) {
+	if name == nil {
+		for _, value := range values {
+			*prop = append(*prop, NamedPath{value, ""})
+		}
+	} else {
+		for _, value := range values {
+			*prop = append(*prop, NamedPath{value, *name})
+		}
+	}
+	*prop = SortedUniqueNamedPaths(*prop)
+}
+
+// Update a property NamedPath array with a distinct union of its values and a list of new values.
+func mergeNamedPathProps(prop *NamedPaths, values ...NamedPath) {
 	*prop = append(*prop, values...)
-	*prop = SortedUniquePaths(*prop)
+	*prop = SortedUniqueNamedPaths(*prop)
 }
 
 // Get the licenses property falling back to the package default.
@@ -316,4 +330,7 @@
 func licensesMakeVarsProvider(ctx MakeVarsContext) {
 	ctx.Strict("BUILD_LICENSE_METADATA",
 		ctx.Config().HostToolPath(ctx, "build_license_metadata").String())
+	ctx.Strict("HTMLNOTICE", ctx.Config().HostToolPath(ctx, "htmlnotice").String())
+	ctx.Strict("XMLNOTICE", ctx.Config().HostToolPath(ctx, "xmlnotice").String())
+	ctx.Strict("TEXTNOTICE", ctx.Config().HostToolPath(ctx, "textnotice").String())
 }
diff --git a/android/licenses_test.go b/android/licenses_test.go
index d05b0a3..8a81e12 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -90,9 +90,9 @@
 			"libother":    []string{"shownotice"},
 		},
 		effectiveNotices: map[string][]string{
-			"libexample1": []string{"top/LICENSE", "top/NOTICE"},
-			"libnested":   []string{"top/LICENSE", "top/NOTICE"},
-			"libother":    []string{"top/LICENSE", "top/NOTICE"},
+			"libexample1": []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
+			"libnested":   []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
+			"libother":    []string{"top/LICENSE:topDog", "top/NOTICE:topDog"},
 		},
 	},
 
@@ -779,9 +779,6 @@
 	base := m.base()
 	m.AddProperties(&base.nameProperties, &m.properties)
 
-	base.generalProperties = m.GetProperties()
-	base.customizableProperties = m.GetProperties()
-
 	// The default_visibility property needs to be checked and parsed by the visibility module during
 	// its checking and parsing phases so make it the primary visibility property.
 	setPrimaryVisibilityProperty(m, "visibility", &m.properties.Visibility)
diff --git a/android/module.go b/android/module.go
index 767f9f4..03d3f80 100644
--- a/android/module.go
+++ b/android/module.go
@@ -16,10 +16,13 @@
 
 import (
 	"fmt"
+	"net/url"
 	"os"
 	"path"
 	"path/filepath"
+	"reflect"
 	"regexp"
+	"sort"
 	"strings"
 	"text/scanner"
 
@@ -266,7 +269,7 @@
 	//
 	// The Modules passed to the visit function should not be retained outside of the visit function, they may be
 	// invalidated by future mutators.
-	WalkDeps(visit func(Module, Module) bool)
+	WalkDeps(visit func(child, parent Module) bool)
 
 	// WalkDepsBlueprint calls visit for each transitive dependency, traversing the dependency
 	// tree in top down order.  visit may be called multiple times for the same (child, parent)
@@ -320,6 +323,9 @@
 	// AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build
 	AddUnconvertedBp2buildDep(dep string)
 
+	// AddMissingBp2buildDep stores the module name of a direct dependency that was not found.
+	AddMissingBp2buildDep(dep string)
+
 	Target() Target
 	TargetPrimary() bool
 
@@ -419,7 +425,6 @@
 	PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec
 
 	CheckbuildFile(srcPath Path)
-	TidyFile(srcPath WritablePath)
 
 	InstallInData() bool
 	InstallInTestcases() bool
@@ -430,7 +435,6 @@
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallInVendor() bool
-	InstallBypassMake() bool
 	InstallForceOS() (*OsType, *ArchType)
 
 	RequiredModuleNames() []string
@@ -497,7 +501,6 @@
 	InstallInRecovery() bool
 	InstallInRoot() bool
 	InstallInVendor() bool
-	InstallBypassMake() bool
 	InstallForceOS() (*OsType, *ArchType)
 	HideFromMake()
 	IsHideFromMake() bool
@@ -519,6 +522,7 @@
 	// Bp2buildTargets returns the target(s) generated for Bazel via bp2build for this module
 	Bp2buildTargets() []bp2buildInfo
 	GetUnconvertedBp2buildDeps() []string
+	GetMissingBp2buildDeps() []string
 
 	BuildParamsForTests() []BuildParams
 	RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
@@ -614,6 +618,53 @@
 	Tag *string `android:"arch_variant"`
 }
 
+// NamedPath associates a path with a name. e.g. a license text path with a package name
+type NamedPath struct {
+	Path Path
+	Name string
+}
+
+// String returns an escaped string representing the `NamedPath`.
+func (p NamedPath) String() string {
+	if len(p.Name) > 0 {
+		return p.Path.String() + ":" + url.QueryEscape(p.Name)
+	}
+	return p.Path.String()
+}
+
+// NamedPaths describes a list of paths each associated with a name.
+type NamedPaths []NamedPath
+
+// Strings returns a list of escaped strings representing each `NamedPath` in the list.
+func (l NamedPaths) Strings() []string {
+	result := make([]string, 0, len(l))
+	for _, p := range l {
+		result = append(result, p.String())
+	}
+	return result
+}
+
+// SortedUniqueNamedPaths modifies `l` in place to return the sorted unique subset.
+func SortedUniqueNamedPaths(l NamedPaths) NamedPaths {
+	if len(l) == 0 {
+		return l
+	}
+	sort.Slice(l, func(i, j int) bool {
+		return l[i].String() < l[j].String()
+	})
+	k := 0
+	for i := 1; i < len(l); i++ {
+		if l[i].String() == l[k].String() {
+			continue
+		}
+		k++
+		if k < i {
+			l[k] = l[i]
+		}
+	}
+	return l[:k+1]
+}
+
 type nameProperties struct {
 	// The name of the module.  Must be unique across all modules.
 	Name *string
@@ -682,7 +733,7 @@
 	// Override of module name when reporting licenses
 	Effective_package_name *string `blueprint:"mutated"`
 	// Notice files
-	Effective_license_text Paths `blueprint:"mutated"`
+	Effective_license_text NamedPaths `blueprint:"mutated"`
 	// License names
 	Effective_license_kinds []string `blueprint:"mutated"`
 	// License conditions
@@ -860,6 +911,9 @@
 	// UnconvertedBp2buildDep stores the module names of direct dependency that were not converted to
 	// Bazel
 	UnconvertedBp2buildDeps []string `blueprint:"mutated"`
+
+	// MissingBp2buildDep stores the module names of direct dependency that were not found
+	MissingBp2buildDeps []string `blueprint:"mutated"`
 }
 
 // CommonAttributes represents the common Bazel attributes from which properties
@@ -872,6 +926,13 @@
 	Data bazel.LabelListAttribute
 }
 
+// constraintAttributes represents Bazel attributes pertaining to build constraints,
+// which make restrict building a Bazel target for some set of platforms.
+type constraintAttributes struct {
+	// Constraint values this target can be built for.
+	Target_compatible_with bazel.LabelListAttribute
+}
+
 type distProperties struct {
 	// configuration to distribute output files from this module to the distribution
 	// directory (default: $OUT/dist, configurable with $DIST_DIR)
@@ -1028,9 +1089,6 @@
 
 	initProductVariableModule(m)
 
-	base.generalProperties = m.GetProperties()
-	base.customizableProperties = m.GetProperties()
-
 	// The default_visibility property needs to be checked and parsed by the visibility module during
 	// its checking and parsing phases so make it the primary visibility property.
 	setPrimaryVisibilityProperty(m, "visibility", &base.commonProperties.Visibility)
@@ -1092,7 +1150,8 @@
 	m.base().commonProperties.CreateCommonOSVariant = true
 }
 
-func (attrs *CommonAttributes) fillCommonBp2BuildModuleAttrs(ctx *topDownMutatorContext) {
+func (attrs *CommonAttributes) fillCommonBp2BuildModuleAttrs(ctx *topDownMutatorContext,
+	enabledPropertyOverrides bazel.BoolAttribute) constraintAttributes {
 	// Assert passed-in attributes include Name
 	name := attrs.Name
 	if len(name) == 0 {
@@ -1110,14 +1169,101 @@
 
 	required := depsToLabelList(props.Required)
 	archVariantProps := mod.GetArchVariantProperties(ctx, &commonProperties{})
+
+	var enabledProperty bazel.BoolAttribute
+	if props.Enabled != nil {
+		enabledProperty.Value = props.Enabled
+	}
+
 	for axis, configToProps := range archVariantProps {
 		for config, _props := range configToProps {
 			if archProps, ok := _props.(*commonProperties); ok {
 				required.SetSelectValue(axis, config, depsToLabelList(archProps.Required).Value)
+				if archProps.Enabled != nil {
+					enabledProperty.SetSelectValue(axis, config, archProps.Enabled)
+				}
 			}
 		}
 	}
+
+	if enabledPropertyOverrides.Value != nil {
+		enabledProperty.Value = enabledPropertyOverrides.Value
+	}
+	for _, axis := range enabledPropertyOverrides.SortedConfigurationAxes() {
+		configToBools := enabledPropertyOverrides.ConfigurableValues[axis]
+		for cfg, val := range configToBools {
+			enabledProperty.SetSelectValue(axis, cfg, &val)
+		}
+	}
+
+	productConfigEnabledLabels := []bazel.Label{}
+	if !proptools.BoolDefault(enabledProperty.Value, true) {
+		// If the module is not enabled by default, then we can check if a
+		// product variable enables it
+		productConfigEnabledLabels = productVariableConfigEnableLabels(ctx)
+
+		if len(productConfigEnabledLabels) > 0 {
+			// In this case, an existing product variable configuration overrides any
+			// module-level `enable: false` definition
+			newValue := true
+			enabledProperty.Value = &newValue
+		}
+	}
+
+	productConfigEnabledAttribute := bazel.MakeLabelListAttribute(bazel.LabelList{
+		productConfigEnabledLabels, nil,
+	})
+
+	moduleSupportsDevice := mod.commonProperties.HostOrDeviceSupported&deviceSupported == deviceSupported
+	if mod.commonProperties.HostOrDeviceSupported != NeitherHostNorDeviceSupported && !moduleSupportsDevice {
+		enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(false))
+	}
+
+	platformEnabledAttribute, err := enabledProperty.ToLabelListAttribute(
+		bazel.LabelList{[]bazel.Label{bazel.Label{Label: "@platforms//:incompatible"}}, nil},
+		bazel.LabelList{[]bazel.Label{}, nil})
+	if err != nil {
+		ctx.ModuleErrorf("Error processing platform enabled attribute: %s", err)
+	}
+
 	data.Append(required)
+
+	constraints := constraintAttributes{}
+	moduleEnableConstraints := bazel.LabelListAttribute{}
+	moduleEnableConstraints.Append(platformEnabledAttribute)
+	moduleEnableConstraints.Append(productConfigEnabledAttribute)
+	constraints.Target_compatible_with = moduleEnableConstraints
+
+	return constraints
+}
+
+// Check product variables for `enabled: true` flag override.
+// Returns a list of the constraint_value targets who enable this override.
+func productVariableConfigEnableLabels(ctx *topDownMutatorContext) []bazel.Label {
+	productVariableProps := ProductVariableProperties(ctx)
+	productConfigEnablingTargets := []bazel.Label{}
+	const propName = "Enabled"
+	if productConfigProps, exists := productVariableProps[propName]; exists {
+		for productConfigProp, prop := range productConfigProps {
+			flag, ok := prop.(*bool)
+			if !ok {
+				ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
+			}
+
+			if *flag {
+				axis := productConfigProp.ConfigurationAxis()
+				targetLabel := axis.SelectKey(productConfigProp.SelectKey())
+				productConfigEnablingTargets = append(productConfigEnablingTargets, bazel.Label{
+					Label: targetLabel,
+				})
+			} else {
+				// TODO(b/210546943): handle negative case where `enabled: false`
+				ctx.ModuleErrorf("`enabled: false` is not currently supported for configuration variables. See b/210546943", proptools.PropertyNameForField(propName))
+			}
+		}
+	}
+
+	return productConfigEnablingTargets
 }
 
 // A ModuleBase object contains the properties that are common to all Android
@@ -1172,17 +1318,14 @@
 	distProperties          distProperties
 	variableProperties      interface{}
 	hostAndDeviceProperties hostAndDeviceProperties
-	generalProperties       []interface{}
 
-	// Arch specific versions of structs in generalProperties. The outer index
-	// has the same order as generalProperties as initialized in
-	// InitAndroidArchModule, and the inner index chooses the props specific to
-	// the architecture. The interface{} value is an archPropRoot that is
-	// filled with arch specific values by the arch mutator.
+	// Arch specific versions of structs in GetProperties() prior to
+	// initialization in InitAndroidArchModule, lets call it `generalProperties`.
+	// The outer index has the same order as generalProperties and the inner index
+	// chooses the props specific to the architecture. The interface{} value is an
+	// archPropRoot that is filled with arch specific values by the arch mutator.
 	archProperties [][]interface{}
 
-	customizableProperties []interface{}
-
 	// Properties specific to the Blueprint to BUILD migration.
 	bazelTargetModuleProperties bazel.BazelTargetModuleProperties
 
@@ -1200,7 +1343,6 @@
 	installFiles         InstallPaths
 	installFilesDepSet   *installPathsDepSet
 	checkbuildFiles      Paths
-	tidyFiles            WritablePaths
 	packagingSpecs       []PackagingSpec
 	packagingSpecsDepSet *packagingSpecsDepSet
 	noticeFiles          Paths
@@ -1216,7 +1358,6 @@
 	// Only set on the final variant of each module
 	installTarget    WritablePath
 	checkbuildTarget WritablePath
-	tidyTarget       WritablePath
 	blueprintDir     string
 
 	hooks hooks
@@ -1230,14 +1371,22 @@
 
 	initRcPaths         Paths
 	vintfFragmentsPaths Paths
+
+	// set of dependency module:location mappings used to populate the license metadata for
+	// apex containers.
+	licenseInstallMap []string
+
+	// The path to the generated license metadata file for the module.
+	licenseMetadataFile WritablePath
 }
 
 // A struct containing all relevant information about a Bazel target converted via bp2build.
 type bp2buildInfo struct {
-	Dir         string
-	BazelProps  bazel.BazelTargetModuleProperties
-	CommonAttrs CommonAttributes
-	Attrs       interface{}
+	Dir             string
+	BazelProps      bazel.BazelTargetModuleProperties
+	CommonAttrs     CommonAttributes
+	ConstraintAttrs constraintAttributes
+	Attrs           interface{}
 }
 
 // TargetName returns the Bazel target name of a bp2build converted target.
@@ -1263,7 +1412,7 @@
 
 // BazelAttributes returns the Bazel attributes of a bp2build converted target.
 func (b bp2buildInfo) BazelAttributes() []interface{} {
-	return []interface{}{&b.CommonAttrs, b.Attrs}
+	return []interface{}{&b.CommonAttrs, &b.ConstraintAttrs, b.Attrs}
 }
 
 func (m *ModuleBase) addBp2buildInfo(info bp2buildInfo) {
@@ -1286,20 +1435,90 @@
 	*unconvertedDeps = append(*unconvertedDeps, dep)
 }
 
+// AddMissingBp2buildDep stores module name of a dependency that was not found in a Android.bp file.
+func (b *baseModuleContext) AddMissingBp2buildDep(dep string) {
+	missingDeps := &b.Module().base().commonProperties.MissingBp2buildDeps
+	*missingDeps = append(*missingDeps, 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
+	return FirstUniqueStrings(m.commonProperties.UnconvertedBp2buildDeps)
+}
+
+// GetMissingBp2buildDeps eturns the list of module names that were not found in Android.bp files.
+func (m *ModuleBase) GetMissingBp2buildDeps() []string {
+	return FirstUniqueStrings(m.commonProperties.MissingBp2buildDeps)
 }
 
 func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
-	(*d)["Android"] = map[string]interface{}{}
+	(*d)["Android"] = map[string]interface{}{
+		// Properties set in Blueprint or in blueprint of a defaults modules
+		"SetProperties": m.propertiesWithValues(),
+	}
+}
+
+type propInfo struct {
+	Name string
+	Type string
+}
+
+func (m *ModuleBase) propertiesWithValues() []propInfo {
+	var info []propInfo
+	props := m.GetProperties()
+
+	var propsWithValues func(name string, v reflect.Value)
+	propsWithValues = func(name string, v reflect.Value) {
+		kind := v.Kind()
+		switch kind {
+		case reflect.Ptr, reflect.Interface:
+			if v.IsNil() {
+				return
+			}
+			propsWithValues(name, v.Elem())
+		case reflect.Struct:
+			if v.IsZero() {
+				return
+			}
+			for i := 0; i < v.NumField(); i++ {
+				namePrefix := name
+				sTyp := v.Type().Field(i)
+				if proptools.ShouldSkipProperty(sTyp) {
+					continue
+				}
+				if name != "" && !strings.HasSuffix(namePrefix, ".") {
+					namePrefix += "."
+				}
+				if !proptools.IsEmbedded(sTyp) {
+					namePrefix += sTyp.Name
+				}
+				sVal := v.Field(i)
+				propsWithValues(namePrefix, sVal)
+			}
+		case reflect.Array, reflect.Slice:
+			if v.IsNil() {
+				return
+			}
+			elKind := v.Type().Elem().Kind()
+			info = append(info, propInfo{name, elKind.String() + " " + kind.String()})
+		default:
+			info = append(info, propInfo{name, kind.String()})
+		}
+	}
+
+	for _, p := range props {
+		propsWithValues("", reflect.ValueOf(p).Elem())
+	}
+	return info
 }
 
 func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
 
 func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
 
+// AddProperties "registers" the provided props
+// each value in props MUST be a pointer to a struct
 func (m *ModuleBase) AddProperties(props ...interface{}) {
 	m.registerProps = append(m.registerProps, props...)
 }
@@ -1631,7 +1850,11 @@
 }
 
 func (m *ModuleBase) EffectiveLicenseFiles() Paths {
-	return m.commonProperties.Effective_license_text
+	result := make(Paths, 0, len(m.commonProperties.Effective_license_text))
+	for _, p := range m.commonProperties.Effective_license_text {
+		result = append(result, p.Path)
+	}
+	return result
 }
 
 // computeInstallDeps finds the installed paths of all dependencies that have a dependency
@@ -1701,10 +1924,6 @@
 	return false
 }
 
-func (m *ModuleBase) InstallBypassMake() bool {
-	return true
-}
-
 func (m *ModuleBase) InstallForceOS() (*OsType, *ArchType) {
 	return nil, nil
 }
@@ -1774,20 +1993,28 @@
 	return append(Paths{}, m.vintfFragmentsPaths...)
 }
 
+func (m *ModuleBase) CompileMultilib() *string {
+	return m.base().commonProperties.Compile_multilib
+}
+
+// SetLicenseInstallMap stores the set of dependency module:location mappings for files in an
+// apex container for use when generation the license metadata file.
+func (m *ModuleBase) SetLicenseInstallMap(installMap []string) {
+	m.licenseInstallMap = append(m.licenseInstallMap, installMap...)
+}
+
 func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) {
 	var allInstalledFiles InstallPaths
 	var allCheckbuildFiles Paths
-	var allTidyFiles WritablePaths
 	ctx.VisitAllModuleVariants(func(module Module) {
 		a := module.base()
 		allInstalledFiles = append(allInstalledFiles, a.installFiles...)
-		// A module's -{checkbuild,tidy} phony targets should
+		// A module's -checkbuild phony targets should
 		// not be created if the module is not exported to make.
 		// Those could depend on the build target and fail to compile
 		// for the current build target.
 		if !ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(a) {
 			allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
-			allTidyFiles = append(allTidyFiles, a.tidyFiles...)
 		}
 	})
 
@@ -1812,13 +2039,6 @@
 		deps = append(deps, m.checkbuildTarget)
 	}
 
-	if len(allTidyFiles) > 0 {
-		name := namespacePrefix + ctx.ModuleName() + "-tidy"
-		ctx.Phony(name, allTidyFiles.Paths()...)
-		m.tidyTarget = PathForPhony(ctx, name)
-		deps = append(deps, m.tidyTarget)
-	}
-
 	if len(deps) > 0 {
 		suffix := ""
 		if ctx.Config().KatiEnabled() {
@@ -1917,6 +2137,8 @@
 		variables:         make(map[string]string),
 	}
 
+	m.licenseMetadataFile = PathForModuleOut(ctx, "meta_lic")
+
 	dependencyInstallFiles, dependencyPackagingSpecs := m.computeInstallDeps(ctx)
 	// set m.installFilesDepSet to only the transitive dependencies to be used as the dependencies
 	// of installed files of this module.  It will be replaced by a depset including the installed
@@ -2027,7 +2249,6 @@
 
 		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...)
 		m.katiInstalls = append(m.katiInstalls, ctx.katiInstalls...)
 		m.katiSymlinks = append(m.katiSymlinks, ctx.katiSymlinks...)
@@ -2049,6 +2270,8 @@
 	m.installFilesDepSet = newInstallPathsDepSet(m.installFiles, dependencyInstallFiles)
 	m.packagingSpecsDepSet = newPackagingSpecsDepSet(m.packagingSpecs, dependencyPackagingSpecs)
 
+	buildLicenseMetadata(ctx, m.licenseMetadataFile)
+
 	m.buildParams = ctx.buildParams
 	m.ruleParams = ctx.ruleParams
 	m.variables = ctx.variables
@@ -2095,18 +2318,18 @@
 	return GlobFiles(e, globPattern, excludes)
 }
 
-func (b *earlyModuleContext) IsSymlink(path Path) bool {
-	fileInfo, err := b.config.fs.Lstat(path.String())
+func (e *earlyModuleContext) IsSymlink(path Path) bool {
+	fileInfo, err := e.config.fs.Lstat(path.String())
 	if err != nil {
-		b.ModuleErrorf("os.Lstat(%q) failed: %s", path.String(), err)
+		e.ModuleErrorf("os.Lstat(%q) failed: %s", path.String(), err)
 	}
 	return fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink
 }
 
-func (b *earlyModuleContext) Readlink(path Path) string {
-	dest, err := b.config.fs.Readlink(path.String())
+func (e *earlyModuleContext) Readlink(path Path) string {
+	dest, err := e.config.fs.Readlink(path.String())
 	if err != nil {
-		b.ModuleErrorf("os.Readlink(%q) failed: %s", path.String(), err)
+		e.ModuleErrorf("os.Readlink(%q) failed: %s", path.String(), err)
 	}
 	return dest
 }
@@ -2225,7 +2448,6 @@
 	packagingSpecs  []PackagingSpec
 	installFiles    InstallPaths
 	checkbuildFiles Paths
-	tidyFiles       WritablePaths
 	module          Module
 	phonies         map[string]Paths
 
@@ -2461,7 +2683,7 @@
 	}
 
 	if aModule == nil {
-		b.ModuleErrorf("module %q not an android module", b.OtherModuleName(module))
+		b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag)
 		return nil
 	}
 
@@ -2583,8 +2805,8 @@
 
 func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) {
 	b.bp.VisitDirectDeps(func(module blueprint.Module) {
-		if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
-			if b.bp.OtherModuleDependencyTag(aModule) == tag {
+		if b.bp.OtherModuleDependencyTag(module) == tag {
+			if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil {
 				visit(aModule)
 			}
 		}
@@ -2829,10 +3051,6 @@
 	return m.module.InstallInRoot()
 }
 
-func (m *moduleContext) InstallBypassMake() bool {
-	return m.module.InstallBypassMake()
-}
-
 func (m *moduleContext) InstallForceOS() (*OsType, *ArchType) {
 	return m.module.InstallForceOS()
 }
@@ -2857,12 +3075,6 @@
 		return true
 	}
 
-	if m.Device() {
-		if m.Config().KatiEnabled() && !m.InstallBypassMake() {
-			return true
-		}
-	}
-
 	return false
 }
 
@@ -2921,7 +3133,7 @@
 			orderOnlyDeps = deps
 		}
 
-		if m.Config().KatiEnabled() && m.InstallBypassMake() {
+		if m.Config().KatiEnabled() {
 			// When creating the install rule in Soong but embedding in Make, write the rule to a
 			// makefile instead of directly to the ninja file so that main.mk can add the
 			// dependencies from the `required` property that are hard to resolve in Soong.
@@ -2980,7 +3192,7 @@
 	}
 	if !m.skipInstall() {
 
-		if m.Config().KatiEnabled() && m.InstallBypassMake() {
+		if m.Config().KatiEnabled() {
 			// When creating the symlink rule in Soong but embedding in Make, write the rule to a
 			// makefile instead of directly to the ninja file so that main.mk can add the
 			// dependencies from the `required` property that are hard to resolve in Soong.
@@ -3026,7 +3238,7 @@
 	m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true)
 
 	if !m.skipInstall() {
-		if m.Config().KatiEnabled() && m.InstallBypassMake() {
+		if m.Config().KatiEnabled() {
 			// When creating the symlink rule in Soong but embedding in Make, write the rule to a
 			// makefile instead of directly to the ninja file so that main.mk can add the
 			// dependencies from the `required` property that are hard to resolve in Soong.
@@ -3063,10 +3275,6 @@
 	m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
 }
 
-func (m *moduleContext) TidyFile(srcPath WritablePath) {
-	m.tidyFiles = append(m.tidyFiles, srcPath)
-}
-
 func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext {
 	return m.bp
 }
@@ -3325,9 +3533,10 @@
 
 type buildTargetSingleton struct{}
 
-func addAncestors(ctx SingletonContext, dirMap map[string]Paths, mmName func(string) string) []string {
+func AddAncestors(ctx SingletonContext, dirMap map[string]Paths, mmName func(string) string) ([]string, []string) {
 	// Ensure ancestor directories are in dirMap
 	// Make directories build their direct subdirectories
+	// Returns a slice of all directories and a slice of top-level directories.
 	dirs := SortedStringKeys(dirMap)
 	for _, dir := range dirs {
 		dir := parentDir(dir)
@@ -3340,34 +3549,31 @@
 		}
 	}
 	dirs = SortedStringKeys(dirMap)
+	var topDirs []string
 	for _, dir := range dirs {
 		p := parentDir(dir)
 		if p != "." && p != "/" {
 			dirMap[p] = append(dirMap[p], PathForPhony(ctx, mmName(dir)))
+		} else if dir != "." && dir != "/" && dir != "" {
+			topDirs = append(topDirs, dir)
 		}
 	}
-	return SortedStringKeys(dirMap)
+	return SortedStringKeys(dirMap), topDirs
 }
 
 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)
@@ -3377,16 +3583,6 @@
 		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 := ""
@@ -3397,24 +3593,12 @@
 	// 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
 	}
 
-	dirs = addAncestors(ctx, modulesInDir, mmTarget)
+	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/module_test.go b/android/module_test.go
index 8607a8d..a1bab6d 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -204,10 +204,6 @@
 	}
 }
 
-func (m *depsModule) InstallBypassMake() bool {
-	return true
-}
-
 func (m *depsModule) GenerateAndroidBuildActions(ctx ModuleContext) {
 	outputFile := PathForModuleOut(ctx, ctx.ModuleName())
 	ctx.Build(pctx, BuildParams{
@@ -450,7 +446,7 @@
 	assertOrderOnlys(symlinkRule("foo"))
 }
 
-func TestInstallBypassMake(t *testing.T) {
+func TestInstallKatiEnabled(t *testing.T) {
 	if runtime.GOOS != "linux" {
 		t.Skip("requires linux")
 	}
@@ -619,3 +615,321 @@
 
 	return rules
 }
+
+type PropsTestModuleEmbedded struct {
+	Embedded_prop *string
+}
+
+type propsTestModule struct {
+	ModuleBase
+	DefaultableModuleBase
+	props struct {
+		A string `android:"arch_variant"`
+		B *bool
+		C []string
+	}
+	otherProps struct {
+		PropsTestModuleEmbedded
+
+		D      *int64
+		Nested struct {
+			E *string
+		}
+		F *string `blueprint:"mutated"`
+	}
+}
+
+func propsTestModuleFactory() Module {
+	module := &propsTestModule{}
+	module.AddProperties(&module.props, &module.otherProps)
+	InitAndroidArchModule(module, HostAndDeviceSupported, MultilibBoth)
+	InitDefaultableModule(module)
+	return module
+}
+
+type propsTestModuleDefaults struct {
+	ModuleBase
+	DefaultsModuleBase
+}
+
+func propsTestModuleDefaultsFactory() Module {
+	defaults := &propsTestModuleDefaults{}
+	module := propsTestModule{}
+	defaults.AddProperties(&module.props, &module.otherProps)
+	InitDefaultsModule(defaults)
+	return defaults
+}
+
+func (p *propsTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	str := "abc"
+	p.otherProps.F = &str
+}
+
+func TestUsedProperties(t *testing.T) {
+	testCases := []struct {
+		desc          string
+		bp            string
+		expectedProps []propInfo
+	}{
+		{
+			desc: "only name",
+			bp: `test {
+			name: "foo",
+		}
+	`,
+			expectedProps: []propInfo{
+				propInfo{"Name", "string"},
+			},
+		},
+		{
+			desc: "some props",
+			bp: `test {
+			name: "foo",
+			a: "abc",
+			b: true,
+			d: 123,
+		}
+	`,
+			expectedProps: []propInfo{
+				propInfo{"A", "string"},
+				propInfo{"B", "bool"},
+				propInfo{"D", "int64"},
+				propInfo{"Name", "string"},
+			},
+		},
+		{
+			desc: "unused non-pointer prop",
+			bp: `test {
+			name: "foo",
+			b: true,
+			d: 123,
+		}
+	`,
+			expectedProps: []propInfo{
+				// for non-pointer cannot distinguish between unused and intentionally set to empty
+				propInfo{"A", "string"},
+				propInfo{"B", "bool"},
+				propInfo{"D", "int64"},
+				propInfo{"Name", "string"},
+			},
+		},
+		{
+			desc: "nested props",
+			bp: `test {
+			name: "foo",
+			nested: {
+				e: "abc",
+			}
+		}
+	`,
+			expectedProps: []propInfo{
+				propInfo{"Nested.E", "string"},
+				propInfo{"Name", "string"},
+			},
+		},
+		{
+			desc: "arch props",
+			bp: `test {
+			name: "foo",
+			arch: {
+				x86_64: {
+					a: "abc",
+				},
+			}
+		}
+	`,
+			expectedProps: []propInfo{
+				propInfo{"Name", "string"},
+				propInfo{"Arch.X86_64.A", "string"},
+			},
+		},
+		{
+			desc: "embedded props",
+			bp: `test {
+			name: "foo",
+			embedded_prop: "a",
+		}
+	`,
+			expectedProps: []propInfo{
+				propInfo{"Embedded_prop", "string"},
+				propInfo{"Name", "string"},
+			},
+		},
+		{
+			desc: "defaults",
+			bp: `
+test_defaults {
+	name: "foo_defaults",
+	a: "a",
+	b: true,
+	embedded_prop:"a",
+	arch: {
+		x86_64: {
+			a: "a",
+		},
+	},
+}
+test {
+	name: "foo",
+	defaults: ["foo_defaults"],
+	c: ["a"],
+	nested: {
+		e: "d",
+	},
+	target: {
+		linux: {
+			a: "a",
+		},
+	},
+}
+	`,
+			expectedProps: []propInfo{
+				propInfo{"A", "string"},
+				propInfo{"B", "bool"},
+				propInfo{"C", "string slice"},
+				propInfo{"Embedded_prop", "string"},
+				propInfo{"Nested.E", "string"},
+				propInfo{"Name", "string"},
+				propInfo{"Arch.X86_64.A", "string"},
+				propInfo{"Target.Linux.A", "string"},
+				propInfo{"Defaults", "string slice"},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			result := GroupFixturePreparers(
+				PrepareForTestWithAllowMissingDependencies,
+				PrepareForTestWithDefaults,
+				FixtureRegisterWithContext(func(ctx RegistrationContext) {
+					ctx.RegisterModuleType("test", propsTestModuleFactory)
+					ctx.RegisterModuleType("test_defaults", propsTestModuleDefaultsFactory)
+				}),
+				FixtureWithRootAndroidBp(tc.bp),
+			).RunTest(t)
+
+			foo := result.ModuleForTests("foo", "").Module().base()
+
+			AssertDeepEquals(t, "foo ", tc.expectedProps, foo.propertiesWithValues())
+
+		})
+	}
+}
+
+func TestSortedUniqueNamedPaths(t *testing.T) {
+	type np struct {
+		path, name string
+	}
+	makePaths := func(l []np) NamedPaths {
+		result := make(NamedPaths, 0, len(l))
+		for _, p := range l {
+			result = append(result, NamedPath{PathForTesting(p.path), p.name})
+		}
+		return result
+	}
+
+	tests := []struct {
+		name        string
+		in          []np
+		expectedOut []np
+	}{
+		{
+			name:        "empty",
+			in:          []np{},
+			expectedOut: []np{},
+		},
+		{
+			name: "all_same",
+			in: []np{
+				{"a.txt", "A"},
+				{"a.txt", "A"},
+				{"a.txt", "A"},
+				{"a.txt", "A"},
+				{"a.txt", "A"},
+			},
+			expectedOut: []np{
+				{"a.txt", "A"},
+			},
+		},
+		{
+			name: "same_path_different_names",
+			in: []np{
+				{"a.txt", "C"},
+				{"a.txt", "A"},
+				{"a.txt", "D"},
+				{"a.txt", "B"},
+				{"a.txt", "E"},
+			},
+			expectedOut: []np{
+				{"a.txt", "A"},
+				{"a.txt", "B"},
+				{"a.txt", "C"},
+				{"a.txt", "D"},
+				{"a.txt", "E"},
+			},
+		},
+		{
+			name: "different_paths_same_name",
+			in: []np{
+				{"b/b.txt", "A"},
+				{"a/a.txt", "A"},
+				{"a/txt", "A"},
+				{"b", "A"},
+				{"a/b/d", "A"},
+			},
+			expectedOut: []np{
+				{"a/a.txt", "A"},
+				{"a/b/d", "A"},
+				{"a/txt", "A"},
+				{"b/b.txt", "A"},
+				{"b", "A"},
+			},
+		},
+		{
+			name: "all_different",
+			in: []np{
+				{"b/b.txt", "A"},
+				{"a/a.txt", "B"},
+				{"a/txt", "D"},
+				{"b", "C"},
+				{"a/b/d", "E"},
+			},
+			expectedOut: []np{
+				{"a/a.txt", "B"},
+				{"a/b/d", "E"},
+				{"a/txt", "D"},
+				{"b/b.txt", "A"},
+				{"b", "C"},
+			},
+		},
+		{
+			name: "some_different",
+			in: []np{
+				{"b/b.txt", "A"},
+				{"a/a.txt", "B"},
+				{"a/txt", "D"},
+				{"a/b/d", "E"},
+				{"b", "C"},
+				{"a/a.txt", "B"},
+				{"a/b/d", "E"},
+			},
+			expectedOut: []np{
+				{"a/a.txt", "B"},
+				{"a/b/d", "E"},
+				{"a/txt", "D"},
+				{"b/b.txt", "A"},
+				{"b", "C"},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			actual := SortedUniqueNamedPaths(makePaths(tt.in))
+			expected := makePaths(tt.expectedOut)
+			t.Logf("actual: %v", actual)
+			t.Logf("expected: %v", expected)
+			AssertDeepEquals(t, "SortedUniqueNamedPaths ", expected, actual)
+		})
+	}
+}
diff --git a/android/mutator.go b/android/mutator.go
index 461cb17..739e4ee 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -16,7 +16,6 @@
 
 import (
 	"reflect"
-	"sync"
 
 	"android/soong/bazel"
 
@@ -34,12 +33,12 @@
 //   continue on to GenerateAndroidBuildActions
 
 // RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing.
-func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators, bp2buildMutators []RegisterMutatorFunc) {
+func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators []RegisterMutatorFunc) {
 	mctx := &registerMutatorsContext{
 		bazelConversionMode: true,
 	}
 
-	bp2buildPreArchMutators = append([]RegisterMutatorFunc{
+	bp2buildMutators := append([]RegisterMutatorFunc{
 		RegisterNamespaceMutator,
 		RegisterDefaultsPreArchMutators,
 		// TODO(b/165114590): this is required to resolve deps that are only prebuilts, but we should
@@ -47,10 +46,7 @@
 		RegisterPrebuiltsPreArchMutators,
 	},
 		preArchMutators...)
-
-	for _, f := range bp2buildPreArchMutators {
-		f(mctx)
-	}
+	bp2buildMutators = append(bp2buildMutators, registerBp2buildConversionMutator)
 
 	// Register bp2build mutators
 	for _, f := range bp2buildMutators {
@@ -216,24 +212,12 @@
 }
 
 var bp2buildPreArchMutators = []RegisterMutatorFunc{}
-var bp2buildMutators = map[string]RegisterMutatorFunc{}
 
-// See http://b/192523357
-var bp2buildLock sync.Mutex
+// A minimal context for Bp2build conversion
+type Bp2buildMutatorContext interface {
+	BazelConversionPathContext
 
-// RegisterBp2BuildMutator registers specially crafted mutators for
-// converting Blueprint/Android modules into special modules that can
-// be code-generated into Bazel BUILD targets.
-//
-// TODO(b/178068862): bring this into TestContext.
-func RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
-	f := func(ctx RegisterMutatorsContext) {
-		ctx.TopDown(moduleType, m)
-	}
-	// Use a lock to avoid a concurrent map write if RegisterBp2BuildMutator is called in parallel
-	bp2buildLock.Lock()
-	defer bp2buildLock.Unlock()
-	bp2buildMutators[moduleType] = f
+	CreateBazelTargetModule(bazel.BazelTargetModuleProperties, CommonAttributes, interface{})
 }
 
 // PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
@@ -270,6 +254,14 @@
 	// BazelTargetModuleProperties containing additional metadata for the
 	// bp2build codegenerator.
 	CreateBazelTargetModule(bazel.BazelTargetModuleProperties, CommonAttributes, interface{})
+
+	// CreateBazelTargetModuleWithRestrictions creates a BazelTargetModule by calling the
+	// factory method, just like in CreateModule, but also requires
+	// BazelTargetModuleProperties containing additional metadata for the
+	// bp2build codegenerator. The generated target is restricted to only be buildable for certain
+	// platforms, as dictated by a given bool attribute: the target will not be buildable in
+	// any platform for which this bool attribute is false.
+	CreateBazelTargetModuleWithRestrictions(bazel.BazelTargetModuleProperties, CommonAttributes, interface{}, bazel.BoolAttribute)
 }
 
 type topDownMutatorContext struct {
@@ -331,13 +323,13 @@
 	// AddVariationDependencies adds deps as dependencies of the current module, but uses the variations
 	// argument to select which variant of the dependency to use.  It returns a slice of modules for
 	// each dependency (some entries may be nil).  A variant of the dependency must exist that matches
-	// the all of the non-local variations of the current module, plus the variations argument.
+	// all the non-local variations of the current module, plus the variations argument.
 	//
 	// If the mutator is parallel (see MutatorHandle.Parallel), this method will pause until the
 	// new dependencies have had the current mutator called on them.  If the mutator is not
 	// parallel this method does not affect the ordering of the current mutator pass, but will
 	// be ordered correctly for all future mutator passes.
-	AddVariationDependencies([]blueprint.Variation, blueprint.DependencyTag, ...string) []blueprint.Module
+	AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag, names ...string) []blueprint.Module
 
 	// AddFarVariationDependencies adds deps as dependencies of the current module, but uses the
 	// variations argument to select which variant of the dependency to use.  It returns a slice of
@@ -518,39 +510,34 @@
 	bazelProps bazel.BazelTargetModuleProperties,
 	commonAttrs CommonAttributes,
 	attrs interface{}) {
-	commonAttrs.fillCommonBp2BuildModuleAttrs(t)
+	t.createBazelTargetModule(bazelProps, commonAttrs, attrs, bazel.BoolAttribute{})
+}
+
+func (t *topDownMutatorContext) CreateBazelTargetModuleWithRestrictions(
+	bazelProps bazel.BazelTargetModuleProperties,
+	commonAttrs CommonAttributes,
+	attrs interface{},
+	enabledProperty bazel.BoolAttribute) {
+	t.createBazelTargetModule(bazelProps, commonAttrs, attrs, enabledProperty)
+}
+
+func (t *topDownMutatorContext) createBazelTargetModule(
+	bazelProps bazel.BazelTargetModuleProperties,
+	commonAttrs CommonAttributes,
+	attrs interface{},
+	enabledProperty bazel.BoolAttribute) {
+	constraintAttributes := commonAttrs.fillCommonBp2BuildModuleAttrs(t, enabledProperty)
 	mod := t.Module()
 	info := bp2buildInfo{
-		Dir:         t.OtherModuleDir(mod),
-		BazelProps:  bazelProps,
-		CommonAttrs: commonAttrs,
-		Attrs:       attrs,
+		Dir:             t.OtherModuleDir(mod),
+		BazelProps:      bazelProps,
+		CommonAttrs:     commonAttrs,
+		ConstraintAttrs: constraintAttributes,
+		Attrs:           attrs,
 	}
 	mod.base().addBp2buildInfo(info)
 }
 
-func (t *topDownMutatorContext) appendPrependHelper(props []interface{},
-	extendFn func([]interface{}, interface{}, proptools.ExtendPropertyFilterFunc) error) {
-	for _, p := range props {
-		err := extendFn(t.Module().base().customizableProperties, p, nil)
-		if err != nil {
-			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
-				t.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
-			} else {
-				panic(err)
-			}
-		}
-	}
-}
-
-func (t *topDownMutatorContext) AppendProperties(props ...interface{}) {
-	t.appendPrependHelper(props, proptools.AppendMatchingProperties)
-}
-
-func (t *topDownMutatorContext) PrependProperties(props ...interface{}) {
-	t.appendPrependHelper(props, proptools.PrependMatchingProperties)
-}
-
 // android.topDownMutatorContext either has to embed blueprint.TopDownMutatorContext, in which case every method that
 // has an overridden version in android.BaseModuleContext has to be manually forwarded to BaseModuleContext to avoid
 // ambiguous method errors, or it has to store a blueprint.TopDownMutatorContext non-embedded, in which case every
diff --git a/android/namespace.go b/android/namespace.go
index 4f727e1..fc7bc29 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -74,7 +74,7 @@
 
 // A NameResolver implements blueprint.NameInterface, and implements the logic to
 // find a module from namespaces based on a query string.
-// A query string can be a module name or can be be "//namespace_path:module_path"
+// A query string can be a module name or can be "//namespace_path:module_path"
 type NameResolver struct {
 	rootNamespace *Namespace
 
diff --git a/android/neverallow.go b/android/neverallow.go
index b36bf04..6f9ae58 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"fmt"
 	"path/filepath"
 	"reflect"
 	"regexp"
@@ -133,7 +134,6 @@
 		NeverAllow().
 			Without("name", "libhidlbase-combined-impl").
 			Without("name", "libhidlbase").
-			Without("name", "libhidlbase_pgo").
 			With("product_variables.enforce_vintf_manifest.cflags", "*").
 			Because("manifest enforcement should be independent of ."),
 
@@ -214,11 +214,8 @@
 	return []Rule{
 		NeverAllow().
 			ModuleType("makefile_goal").
-			// TODO(b/33691272): remove this after migrating seapp to Soong
-			Without("product_out_path", "obj/ETC/plat_seapp_contexts_intermediates/plat_seapp_contexts").
-			Without("product_out_path", "obj/ETC/plat_seapp_neverallows_intermediates/plat_seapp_neverallows").
 			WithoutMatcher("product_out_path", Regexp("^boot[0-9a-zA-Z.-]*[.]img$")).
-			Because("Only boot images and seapp contexts may be imported as a makefile goal."),
+			Because("Only boot images may be imported as a makefile goal."),
 	}
 }
 
@@ -252,7 +249,7 @@
 			continue
 		}
 
-		if !n.appliesToProperties(properties) {
+		if !n.appliesToProperties(ctx, properties) {
 			continue
 		}
 
@@ -272,8 +269,12 @@
 	}
 }
 
+type ValueMatcherContext interface {
+	Config() Config
+}
+
 type ValueMatcher interface {
-	Test(string) bool
+	Test(ValueMatcherContext, string) bool
 	String() string
 }
 
@@ -281,7 +282,7 @@
 	expected string
 }
 
-func (m *equalMatcher) Test(value string) bool {
+func (m *equalMatcher) Test(ctx ValueMatcherContext, value string) bool {
 	return m.expected == value
 }
 
@@ -292,7 +293,7 @@
 type anyMatcher struct {
 }
 
-func (m *anyMatcher) Test(value string) bool {
+func (m *anyMatcher) Test(ctx ValueMatcherContext, value string) bool {
 	return true
 }
 
@@ -306,7 +307,7 @@
 	prefix string
 }
 
-func (m *startsWithMatcher) Test(value string) bool {
+func (m *startsWithMatcher) Test(ctx ValueMatcherContext, value string) bool {
 	return strings.HasPrefix(value, m.prefix)
 }
 
@@ -318,7 +319,7 @@
 	re *regexp.Regexp
 }
 
-func (m *regexMatcher) Test(value string) bool {
+func (m *regexMatcher) Test(ctx ValueMatcherContext, value string) bool {
 	return m.re.MatchString(value)
 }
 
@@ -330,7 +331,7 @@
 	allowed []string
 }
 
-func (m *notInListMatcher) Test(value string) bool {
+func (m *notInListMatcher) Test(ctx ValueMatcherContext, value string) bool {
 	return !InList(value, m.allowed)
 }
 
@@ -340,7 +341,7 @@
 
 type isSetMatcher struct{}
 
-func (m *isSetMatcher) Test(value string) bool {
+func (m *isSetMatcher) Test(ctx ValueMatcherContext, value string) bool {
 	return value != ""
 }
 
@@ -350,11 +351,38 @@
 
 var isSetMatcherInstance = &isSetMatcher{}
 
+type sdkVersionMatcher struct {
+	condition   func(ctx ValueMatcherContext, spec SdkSpec) bool
+	description string
+}
+
+func (m *sdkVersionMatcher) Test(ctx ValueMatcherContext, value string) bool {
+	return m.condition(ctx, SdkSpecFromWithConfig(ctx.Config(), value))
+}
+
+func (m *sdkVersionMatcher) String() string {
+	return ".sdk-version(" + m.description + ")"
+}
+
 type ruleProperty struct {
 	fields  []string // e.x.: Vndk.Enabled
 	matcher ValueMatcher
 }
 
+func (r *ruleProperty) String() string {
+	return fmt.Sprintf("%q matches: %s", strings.Join(r.fields, "."), r.matcher)
+}
+
+type ruleProperties []ruleProperty
+
+func (r ruleProperties) String() string {
+	var s []string
+	for _, r := range r {
+		s = append(s, r.String())
+	}
+	return strings.Join(s, " ")
+}
+
 // A NeverAllow rule.
 type Rule interface {
 	In(path ...string) Rule
@@ -396,8 +424,8 @@
 	moduleTypes       []string
 	unlessModuleTypes []string
 
-	props       []ruleProperty
-	unlessProps []ruleProperty
+	props       ruleProperties
+	unlessProps ruleProperties
 
 	onlyBootclasspathJar bool
 }
@@ -407,16 +435,19 @@
 	return &rule{directDeps: make(map[string]bool)}
 }
 
+// In adds path(s) where this rule applies.
 func (r *rule) In(path ...string) Rule {
 	r.paths = append(r.paths, cleanPaths(path)...)
 	return r
 }
 
+// NotIn adds path(s) to that this rule does not apply to.
 func (r *rule) NotIn(path ...string) Rule {
 	r.unlessPaths = append(r.unlessPaths, cleanPaths(path)...)
 	return r
 }
 
+// InDirectDeps adds dep(s) that are not allowed with this rule.
 func (r *rule) InDirectDeps(deps ...string) Rule {
 	for _, d := range deps {
 		r.directDeps[d] = true
@@ -424,25 +455,30 @@
 	return r
 }
 
+// WithOsClass adds osClass(es) that this rule applies to.
 func (r *rule) WithOsClass(osClasses ...OsClass) Rule {
 	r.osClasses = append(r.osClasses, osClasses...)
 	return r
 }
 
+// ModuleType adds type(s) that this rule applies to.
 func (r *rule) ModuleType(types ...string) Rule {
 	r.moduleTypes = append(r.moduleTypes, types...)
 	return r
 }
 
+// NotModuleType adds type(s) that this rule does not apply to..
 func (r *rule) NotModuleType(types ...string) Rule {
 	r.unlessModuleTypes = append(r.unlessModuleTypes, types...)
 	return r
 }
 
+// With specifies property/value combinations that are restricted for this rule.
 func (r *rule) With(properties, value string) Rule {
 	return r.WithMatcher(properties, selectMatcher(value))
 }
 
+// WithMatcher specifies property/matcher combinations that are restricted for this rule.
 func (r *rule) WithMatcher(properties string, matcher ValueMatcher) Rule {
 	r.props = append(r.props, ruleProperty{
 		fields:  fieldNamesForProperties(properties),
@@ -451,10 +487,12 @@
 	return r
 }
 
+// Without specifies property/value combinations that this rule does not apply to.
 func (r *rule) Without(properties, value string) Rule {
 	return r.WithoutMatcher(properties, selectMatcher(value))
 }
 
+// Without specifies property/matcher combinations that this rule does not apply to.
 func (r *rule) WithoutMatcher(properties string, matcher ValueMatcher) Rule {
 	r.unlessProps = append(r.unlessProps, ruleProperty{
 		fields:  fieldNamesForProperties(properties),
@@ -470,49 +508,54 @@
 	return &equalMatcher{expected: expected}
 }
 
+// Because specifies a reason for this rule.
 func (r *rule) Because(reason string) Rule {
 	r.reason = reason
 	return r
 }
 
+// BootclasspathJar whether this rule only applies to Jars in the Bootclasspath
 func (r *rule) BootclasspathJar() Rule {
 	r.onlyBootclasspathJar = true
 	return r
 }
 
 func (r *rule) String() string {
-	s := "neverallow"
-	for _, v := range r.paths {
-		s += " dir:" + v + "*"
+	s := []string{"neverallow requirements. Not allowed:"}
+	if len(r.paths) > 0 {
+		s = append(s, fmt.Sprintf("in dirs: %q", r.paths))
 	}
-	for _, v := range r.unlessPaths {
-		s += " -dir:" + v + "*"
+	if len(r.moduleTypes) > 0 {
+		s = append(s, fmt.Sprintf("module types: %q", r.moduleTypes))
 	}
-	for _, v := range r.moduleTypes {
-		s += " type:" + v
+	if len(r.props) > 0 {
+		s = append(s, fmt.Sprintf("properties matching: %s", r.props))
 	}
-	for _, v := range r.unlessModuleTypes {
-		s += " -type:" + v
+	if len(r.directDeps) > 0 {
+		s = append(s, fmt.Sprintf("dep(s): %q", SortedStringKeys(r.directDeps)))
 	}
-	for _, v := range r.props {
-		s += " " + strings.Join(v.fields, ".") + v.matcher.String()
-	}
-	for _, v := range r.unlessProps {
-		s += " -" + strings.Join(v.fields, ".") + v.matcher.String()
-	}
-	for k := range r.directDeps {
-		s += " deps:" + k
-	}
-	for _, v := range r.osClasses {
-		s += " os:" + v.String()
+	if len(r.osClasses) > 0 {
+		s = append(s, fmt.Sprintf("os class(es): %q", r.osClasses))
 	}
 	if r.onlyBootclasspathJar {
-		s += " inBcp"
+		s = append(s, "in bootclasspath jar")
+	}
+	if len(r.unlessPaths) > 0 {
+		s = append(s, fmt.Sprintf("EXCEPT in dirs: %q", r.unlessPaths))
+	}
+	if len(r.unlessModuleTypes) > 0 {
+		s = append(s, fmt.Sprintf("EXCEPT module types: %q", r.unlessModuleTypes))
+	}
+	if len(r.unlessProps) > 0 {
+		s = append(s, fmt.Sprintf("EXCEPT properties matching: %q", r.unlessProps))
 	}
 	if len(r.reason) != 0 {
-		s += " which is restricted because " + r.reason
+		s = append(s, " which is restricted because "+r.reason)
 	}
-	return s
+	if len(s) == 1 {
+		s[0] = "neverallow requirements (empty)"
+	}
+	return strings.Join(s, "\n\t")
 }
 
 func (r *rule) appliesToPath(dir string) bool {
@@ -563,9 +606,10 @@
 	return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
 }
 
-func (r *rule) appliesToProperties(properties []interface{}) bool {
-	includeProps := hasAllProperties(properties, r.props)
-	excludeProps := hasAnyProperty(properties, r.unlessProps)
+func (r *rule) appliesToProperties(ctx ValueMatcherContext,
+	properties []interface{}) bool {
+	includeProps := hasAllProperties(ctx, properties, r.props)
+	excludeProps := hasAnyProperty(ctx, properties, r.unlessProps)
 	return includeProps && !excludeProps
 }
 
@@ -585,6 +629,16 @@
 	return &notInListMatcher{allowed}
 }
 
+func LessThanSdkVersion(sdk string) ValueMatcher {
+	return &sdkVersionMatcher{
+		condition: func(ctx ValueMatcherContext, spec SdkSpec) bool {
+			return spec.ApiLevel.LessThan(
+				SdkSpecFromWithConfig(ctx.Config(), sdk).ApiLevel)
+		},
+		description: "lessThan=" + sdk,
+	}
+}
+
 // assorted utils
 
 func cleanPaths(paths []string) []string {
@@ -603,25 +657,28 @@
 	return names
 }
 
-func hasAnyProperty(properties []interface{}, props []ruleProperty) bool {
+func hasAnyProperty(ctx ValueMatcherContext, properties []interface{},
+	props []ruleProperty) bool {
 	for _, v := range props {
-		if hasProperty(properties, v) {
+		if hasProperty(ctx, properties, v) {
 			return true
 		}
 	}
 	return false
 }
 
-func hasAllProperties(properties []interface{}, props []ruleProperty) bool {
+func hasAllProperties(ctx ValueMatcherContext, properties []interface{},
+	props []ruleProperty) bool {
 	for _, v := range props {
-		if !hasProperty(properties, v) {
+		if !hasProperty(ctx, properties, v) {
 			return false
 		}
 	}
 	return true
 }
 
-func hasProperty(properties []interface{}, prop ruleProperty) bool {
+func hasProperty(ctx ValueMatcherContext, properties []interface{},
+	prop ruleProperty) bool {
 	for _, propertyStruct := range properties {
 		propertiesValue := reflect.ValueOf(propertyStruct).Elem()
 		for _, v := range prop.fields {
@@ -635,7 +692,7 @@
 		}
 
 		check := func(value string) bool {
-			return prop.matcher.Test(value)
+			return prop.matcher.Test(ctx, value)
 		}
 
 		if matchValue(propertiesValue, check) {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index edda244..59016d4 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"regexp"
 	"testing"
 
 	"github.com/google/blueprint"
@@ -55,7 +56,37 @@
 				}`),
 		},
 		expectedErrors: []string{
-			`module "libother": violates neverallow deps:not_allowed_in_direct_deps`,
+			regexp.QuoteMeta("module \"libother\": violates neverallow requirements. Not allowed:\n\tdep(s): [\"not_allowed_in_direct_deps\"]"),
+		},
+	},
+	{
+		name: "multiple constraints",
+		rules: []Rule{
+			NeverAllow().
+				InDirectDeps("not_allowed_in_direct_deps").
+				In("other").
+				ModuleType("cc_library").
+				NotIn("top").
+				NotModuleType("cc_binary"),
+		},
+		fs: map[string][]byte{
+			"top/Android.bp": []byte(`
+				cc_library {
+					name: "not_allowed_in_direct_deps",
+				}`),
+			"other/Android.bp": []byte(`
+				cc_library {
+					name: "libother",
+					static_libs: ["not_allowed_in_direct_deps"],
+				}`),
+		},
+		expectedErrors: []string{
+			regexp.QuoteMeta(`module "libother": violates neverallow requirements. Not allowed:
+	in dirs: ["other/"]
+	module types: ["cc_library"]
+	dep(s): ["not_allowed_in_direct_deps"]
+	EXCEPT in dirs: ["top/"]
+	EXCEPT module types: ["cc_binary"]`),
 		},
 	},
 
@@ -293,7 +324,49 @@
 			`),
 		},
 		expectedErrors: []string{
-			"Only boot images and seapp contexts may be imported as a makefile goal.",
+			"Only boot images may be imported as a makefile goal.",
+		},
+	},
+	{
+		name: "min_sdk too low",
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+				java_library {
+					name: "min_sdk_too_low",
+					min_sdk_version: "30",
+				}`),
+		},
+		rules: []Rule{
+			NeverAllow().WithMatcher("min_sdk_version", LessThanSdkVersion("31")),
+		},
+		expectedErrors: []string{
+			"module \"min_sdk_too_low\": violates neverallow",
+		},
+	},
+	{
+		name: "min_sdk high enough",
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+				java_library {
+					name: "min_sdk_high_enough",
+					min_sdk_version: "31",
+				}`),
+		},
+		rules: []Rule{
+			NeverAllow().WithMatcher("min_sdk_version", LessThanSdkVersion("31")),
+		},
+	},
+	{
+		name: "current min_sdk high enough",
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+				java_library {
+					name: "current_min_sdk_high_enough",
+					min_sdk_version: "current",
+				}`),
+		},
+		rules: []Rule{
+			NeverAllow().WithMatcher("min_sdk_version", LessThanSdkVersion("31")),
 		},
 	},
 }
@@ -379,9 +452,10 @@
 }
 
 type mockJavaLibraryProperties struct {
-	Libs           []string
-	Sdk_version    *string
-	Uncompress_dex *bool
+	Libs            []string
+	Min_sdk_version *string
+	Sdk_version     *string
+	Uncompress_dex  *bool
 }
 
 type mockJavaLibraryModule struct {
diff --git a/android/notices.go b/android/notices.go
index d8cfaf2..194a734 100644
--- a/android/notices.go
+++ b/android/notices.go
@@ -16,6 +16,7 @@
 
 import (
 	"path/filepath"
+	"strings"
 
 	"github.com/google/blueprint"
 )
@@ -101,55 +102,15 @@
 	}
 }
 
-// BuildNotices merges the supplied NOTICE files into a single file that lists notices
-// for every key in noticeMap (which would normally be installed files).
-func BuildNotices(ctx ModuleContext, noticeMap map[string]Paths) NoticeOutputs {
-	// TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass.
-	//
-	// generate-notice-files.py, which processes the merged NOTICE file, has somewhat strict rules
-	// about input NOTICE file paths.
-	// 1. Their relative paths to the src root become their NOTICE index titles. We want to use
-	// on-device paths as titles, and so output the merged NOTICE file the corresponding location.
-	// 2. They must end with .txt extension. Otherwise, they're ignored.
-
-	mergeTool := PathForSource(ctx, "build/soong/scripts/mergenotice.py")
-	generateNoticeTool := PathForSource(ctx, "build/soong/scripts/generate-notice-files.py")
-
-	outputDir := PathForModuleOut(ctx, "notices")
-	builder := NewRuleBuilder(pctx, ctx).
-		Sbox(outputDir, PathForModuleOut(ctx, "notices.sbox.textproto"))
-	for _, installPath := range SortedStringKeys(noticeMap) {
-		noticePath := outputDir.Join(ctx, installPath+".txt")
-		// It would be nice if sbox created directories for temporaries, but until then
-		// this is simple enough.
-		builder.Command().
-			Text("(cd").OutputDir().Text("&&").
-			Text("mkdir -p").Text(filepath.Dir(installPath)).Text(")")
-		builder.Temporary(noticePath)
-		builder.Command().
-			Tool(mergeTool).
-			Flag("--output").Output(noticePath).
-			Inputs(noticeMap[installPath])
-	}
-
-	// Transform the merged NOTICE file into a gzipped HTML file.
-	txtOutput := outputDir.Join(ctx, "NOTICE.txt")
-	htmlOutput := outputDir.Join(ctx, "NOTICE.html")
-	htmlGzOutput := outputDir.Join(ctx, "NOTICE.html.gz")
-	title := "\"Notices for " + ctx.ModuleName() + "\""
-	builder.Command().Tool(generateNoticeTool).
-		FlagWithOutput("--text-output ", txtOutput).
-		FlagWithOutput("--html-output ", htmlOutput).
-		FlagWithArg("-t ", title).
-		Flag("-s").OutputDir()
-	builder.Command().BuiltTool("minigzip").
-		FlagWithInput("-c ", htmlOutput).
-		FlagWithOutput("> ", htmlGzOutput)
-	builder.Build("build_notices", "generate notice output")
-
-	return NoticeOutputs{
-		TxtOutput:    OptionalPathForPath(txtOutput),
-		HtmlOutput:   OptionalPathForPath(htmlOutput),
-		HtmlGzOutput: OptionalPathForPath(htmlGzOutput),
-	}
+// BuildNoticeTextOutputFromLicenseMetadata writes out a notice text file based on the module's
+// generated license metadata file.
+func BuildNoticeTextOutputFromLicenseMetadata(ctx ModuleContext, outputFile WritablePath) {
+	depsFile := outputFile.ReplaceExtension(ctx, strings.TrimPrefix(outputFile.Ext()+".d", "."))
+	rule := NewRuleBuilder(pctx, ctx)
+	rule.Command().
+		BuiltTool("textnotice").
+		FlagWithOutput("-o ", outputFile).
+		FlagWithDepFile("-d ", depsFile).
+		Input(ctx.Module().base().licenseMetadataFile)
+	rule.Build("container_notice", "container notice file")
 }
diff --git a/android/package_ctx.go b/android/package_ctx.go
index c19debb..f354db8 100644
--- a/android/package_ctx.go
+++ b/android/package_ctx.go
@@ -19,6 +19,7 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 
 	"android/soong/remoteexec"
 )
@@ -173,7 +174,7 @@
 // package-scoped variable's initialization.
 func (p PackageContext) HostBinToolVariable(name, path string) blueprint.Variable {
 	return p.VariableFunc(name, func(ctx PackageVarContext) string {
-		return ctx.Config().HostToolPath(ctx, path).String()
+		return proptools.NinjaAndShellEscape(ctx.Config().HostToolPath(ctx, path).String())
 	})
 }
 
@@ -183,7 +184,7 @@
 // package-scoped variable's initialization.
 func (p PackageContext) HostJNIToolVariable(name, path string) blueprint.Variable {
 	return p.VariableFunc(name, func(ctx PackageVarContext) string {
-		return ctx.Config().HostJNIToolPath(ctx, path).String()
+		return proptools.NinjaAndShellEscape(ctx.Config().HostJNIToolPath(ctx, path).String())
 	})
 }
 
@@ -193,7 +194,7 @@
 // part of a package-scoped variable's initialization.
 func (p PackageContext) HostJavaToolVariable(name, path string) blueprint.Variable {
 	return p.VariableFunc(name, func(ctx PackageVarContext) string {
-		return ctx.Config().HostJavaToolPath(ctx, path).String()
+		return proptools.NinjaAndShellEscape(ctx.Config().HostJavaToolPath(ctx, path).String())
 	})
 }
 
diff --git a/android/package_test.go b/android/package_test.go
index 7ea10a4..65c4240 100644
--- a/android/package_test.go
+++ b/android/package_test.go
@@ -11,7 +11,7 @@
 }{
 	// Package default_visibility handling is tested in visibility_test.go
 	{
-		name: "package must not accept visibility and name properties",
+		name: "package must not accept visibility, name or licenses properties",
 		fs: map[string][]byte{
 			"top/Android.bp": []byte(`
 				package {
@@ -48,8 +48,7 @@
 					default_visibility: ["//visibility:private"],
 					default_applicable_licenses: ["license"],
 				}
-
-			        package {
+				package {
 				}`),
 		},
 		expectedErrors: []string{
diff --git a/android/path_properties.go b/android/path_properties.go
index 3976880..fdc4d91 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -33,7 +33,7 @@
 // The pathDepsMutator automatically adds dependencies on any module that is listed with the
 // ":module" module reference syntax in a property that is tagged with `android:"path"`.
 func pathDepsMutator(ctx BottomUpMutatorContext) {
-	props := ctx.Module().base().generalProperties
+	props := ctx.Module().base().GetProperties()
 	addPathDepsForProps(ctx, props)
 }
 
diff --git a/android/paths.go b/android/paths.go
index e68106c..4c69de7 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -112,7 +112,6 @@
 	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
-	InstallBypassMake() bool
 	InstallForceOS() (*OsType, *ArchType)
 }
 
@@ -465,9 +464,6 @@
 // PathForGoBinary returns the path to the installed location of a bootstrap_go_binary module.
 func PathForGoBinary(ctx PathContext, goBinary bootstrap.GoBinaryTool) Path {
 	goBinaryInstallDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin", false)
-	if ctx.Config().KatiEnabled() {
-		goBinaryInstallDir = goBinaryInstallDir.ToMakePath()
-	}
 	rel := Rel(ctx, goBinaryInstallDir.String(), goBinary.InstallPath())
 	return goBinaryInstallDir.Join(ctx, rel)
 }
@@ -1646,8 +1642,8 @@
 	return p
 }
 
-// ToMakePath returns a new InstallPath that points to Make's install directory instead of Soong's,
-// i.e. out/ instead of out/soong/.
+// Deprecated: ToMakePath is a noop, PathForModuleInstall always returns Make paths when building
+// embedded in Make.
 func (p InstallPath) ToMakePath() InstallPath {
 	p.makePath = true
 	return p
@@ -1688,9 +1684,6 @@
 
 func makePathForInstall(ctx ModuleInstallPathContext, os OsType, arch ArchType, partition string, debug bool, pathComponents ...string) InstallPath {
 	ret := pathForInstall(ctx, os, arch, partition, debug, pathComponents...)
-	if ctx.InstallBypassMake() && ctx.Config().KatiEnabled() {
-		ret = ret.ToMakePath()
-	}
 	return ret
 }
 
@@ -1732,7 +1725,10 @@
 		soongOutDir:  ctx.Config().soongOutDir,
 		partitionDir: partionPath,
 		partition:    partition,
-		makePath:     false,
+	}
+
+	if ctx.Config().KatiEnabled() {
+		base.makePath = true
 	}
 
 	return base.Join(ctx, pathComponents...)
@@ -2008,10 +2004,6 @@
 	return m.inRoot
 }
 
-func (m testModuleInstallPathContext) InstallBypassMake() bool {
-	return false
-}
-
 func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
 	return m.forceOS, m.forceArch
 }
@@ -2157,3 +2149,23 @@
 	}
 	return false
 }
+
+// PathsDepSet is a thin type-safe wrapper around the generic depSet.  It always uses
+// topological order.
+type PathsDepSet struct {
+	depSet
+}
+
+// newPathsDepSet returns an immutable PathsDepSet with the given direct and
+// transitive contents.
+func newPathsDepSet(direct Paths, transitive []*PathsDepSet) *PathsDepSet {
+	return &PathsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)}
+}
+
+// ToList returns the PathsDepSet flattened to a list in topological order.
+func (d *PathsDepSet) ToList() Paths {
+	if d == nil {
+		return nil
+	}
+	return d.depSet.ToList().(Paths)
+}
diff --git a/android/paths_test.go b/android/paths_test.go
index 3cad852..2f87977 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -1486,7 +1486,8 @@
 		AssertPathRelativeToTopEquals(t, "install path for soong", "out/soong/target/product/test_device/system/install/path", p)
 	})
 	t.Run("install for make", func(t *testing.T) {
-		p := PathForModuleInstall(ctx, "install/path").ToMakePath()
+		p := PathForModuleInstall(ctx, "install/path")
+		p.makePath = true
 		AssertPathRelativeToTopEquals(t, "install path for make", "out/target/product/test_device/system/install/path", p)
 	})
 	t.Run("output", func(t *testing.T) {
@@ -1500,14 +1501,12 @@
 	t.Run("mixture", func(t *testing.T) {
 		paths := Paths{
 			PathForModuleInstall(ctx, "install/path"),
-			PathForModuleInstall(ctx, "install/path").ToMakePath(),
 			PathForOutput(ctx, "output/path"),
 			PathForSource(ctx, "source/path"),
 		}
 
 		expected := []string{
 			"out/soong/target/product/test_device/system/install/path",
-			"out/target/product/test_device/system/install/path",
 			"out/soong/output/path",
 			"source/path",
 		}
diff --git a/android/prebuilt.go b/android/prebuilt.go
index b0a4f43..5843487 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -183,7 +183,6 @@
 func initPrebuiltModuleCommon(module PrebuiltInterface) *Prebuilt {
 	p := module.Prebuilt()
 	module.AddProperties(&p.properties)
-	module.base().customizableProperties = module.GetProperties()
 	return p
 }
 
@@ -310,6 +309,54 @@
 	return nil
 }
 
+// PrebuiltGetPreferred returns the module that is preferred for the given
+// module. That is either the module itself or the prebuilt counterpart that has
+// taken its place. The given module must be a direct dependency of the current
+// context module, and it must be the source module if both source and prebuilt
+// exist.
+//
+// This function is for use on dependencies after PrebuiltPostDepsMutator has
+// run - any dependency that is registered before that will already reference
+// the right module. This function is only safe to call after all mutators that
+// may call CreateVariations, e.g. in GenerateAndroidBuildActions.
+func PrebuiltGetPreferred(ctx BaseModuleContext, module Module) Module {
+	if !module.IsReplacedByPrebuilt() {
+		return module
+	}
+	if IsModulePrebuilt(module) {
+		// If we're given a prebuilt then assume there's no source module around.
+		return module
+	}
+
+	sourceModDepFound := false
+	var prebuiltMod Module
+
+	ctx.WalkDeps(func(child, parent Module) bool {
+		if prebuiltMod != nil {
+			return false
+		}
+		if parent == ctx.Module() {
+			// First level: Only recurse if the module is found as a direct dependency.
+			sourceModDepFound = child == module
+			return sourceModDepFound
+		}
+		// Second level: Follow PrebuiltDepTag to the prebuilt.
+		if t := ctx.OtherModuleDependencyTag(child); t == PrebuiltDepTag {
+			prebuiltMod = child
+		}
+		return false
+	})
+
+	if prebuiltMod == nil {
+		if !sourceModDepFound {
+			panic(fmt.Errorf("Failed to find source module as a direct dependency: %s", module))
+		} else {
+			panic(fmt.Errorf("Failed to find prebuilt for source module: %s", module))
+		}
+	}
+	return prebuiltMod
+}
+
 func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).Parallel()
 }
diff --git a/android/proto.go b/android/proto.go
index 0be7893..f466261 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -15,12 +15,17 @@
 package android
 
 import (
+	"android/soong/bazel"
 	"strings"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
 
+const (
+	canonicalPathFromRootDefault = true
+)
+
 // TODO(ccross): protos are often used to communicate between multiple modules.  If the only
 // way to convert a proto to source is to reference it as a source file, and external modules cannot
 // reference source files in other modules, then every module that owns a proto file will need to
@@ -90,7 +95,7 @@
 		Flags:                 flags,
 		Deps:                  deps,
 		OutTypeFlag:           protoOutFlag,
-		CanonicalPathFromRoot: proptools.BoolDefault(p.Proto.Canonical_path_from_root, true),
+		CanonicalPathFromRoot: proptools.BoolDefault(p.Proto.Canonical_path_from_root, canonicalPathFromRootDefault),
 		Dir:                   PathForModuleGen(ctx, "proto"),
 		SubDir:                PathForModuleGen(ctx, "proto", ctx.ModuleDir()),
 	}
@@ -146,3 +151,57 @@
 	rule.Command().
 		BuiltTool("dep_fixer").Flag(depFile.String())
 }
+
+// Bp2buildProtoInfo contains information necessary to pass on to language specific conversion.
+type Bp2buildProtoInfo struct {
+	Type *string
+	Name string
+}
+
+type protoAttrs struct {
+	Srcs                bazel.LabelListAttribute
+	Strip_import_prefix *string
+}
+
+// Bp2buildProtoProperties converts proto properties, creating a proto_library and returning the
+// information necessary for language-specific handling.
+func Bp2buildProtoProperties(ctx Bp2buildMutatorContext, module Module, srcs bazel.LabelListAttribute) (Bp2buildProtoInfo, bool) {
+	var info Bp2buildProtoInfo
+	if srcs.IsEmpty() {
+		return info, false
+	}
+	m := module.base()
+
+	info.Name = m.Name() + "_proto"
+	attrs := protoAttrs{
+		Srcs: srcs,
+	}
+
+	for axis, configToProps := range m.GetArchVariantProperties(ctx, &ProtoProperties{}) {
+		for _, rawProps := range configToProps {
+			var props *ProtoProperties
+			var ok bool
+			if props, ok = rawProps.(*ProtoProperties); !ok {
+				ctx.ModuleErrorf("Could not cast ProtoProperties to expected type")
+			}
+			if axis == bazel.NoConfigAxis {
+				info.Type = props.Proto.Type
+
+				if !proptools.BoolDefault(props.Proto.Canonical_path_from_root, canonicalPathFromRootDefault) {
+					// an empty string indicates to strips the package path
+					path := ""
+					attrs.Strip_import_prefix = &path
+				}
+			} else if props.Proto.Type != info.Type && props.Proto.Type != nil {
+				ctx.ModuleErrorf("Cannot handle arch-variant types for protos at this time.")
+			}
+		}
+	}
+
+	ctx.CreateBazelTargetModule(
+		bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
+		CommonAttributes{Name: info.Name},
+		&attrs)
+
+	return info, true
+}
diff --git a/android/register.go b/android/register.go
index 4244398..10e14e0 100644
--- a/android/register.go
+++ b/android/register.go
@@ -31,7 +31,7 @@
 type sortableComponent interface {
 	// componentName returns the name of the component.
 	//
-	// Uniquely identifies the components within the set of components used at runtimr and during
+	// Uniquely identifies the components within the set of components used at runtime and during
 	// tests.
 	componentName() string
 
@@ -178,13 +178,7 @@
 		t.register(ctx)
 	}
 
-	bp2buildMutatorList := []RegisterMutatorFunc{}
-	for t, f := range bp2buildMutators {
-		ctx.config.bp2buildModuleTypeConfig[t] = true
-		bp2buildMutatorList = append(bp2buildMutatorList, f)
-	}
-
-	RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators, bp2buildMutatorList)
+	RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators)
 }
 
 // Register the pipeline of singletons, module types, and mutators for
@@ -196,15 +190,6 @@
 		t.register(ctx)
 	}
 
-	if ctx.config.BazelContext.BazelEnabled() {
-		// Hydrate the configuration of bp2build-enabled module types. This is
-		// required as a signal to identify which modules should be deferred to
-		// Bazel in mixed builds, if it is enabled.
-		for t, _ := range bp2buildMutators {
-			ctx.config.bp2buildModuleTypeConfig[t] = true
-		}
-	}
-
 	mutators := collateGloballyRegisteredMutators()
 	mutators.registerAll(ctx)
 
diff --git a/android/rule_builder.go b/android/rule_builder.go
index f8de5fb..098c1fc 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -470,7 +470,7 @@
 
 func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderCommand {
 	return r.Command().
-		BuiltTool("dep_fixer").
+		builtToolWithoutDeps("dep_fixer").
 		Inputs(depFiles.Paths())
 }
 
@@ -638,7 +638,7 @@
 		}
 		sboxCmd.Text("rm -rf").Output(r.outDir)
 		sboxCmd.Text("&&")
-		sboxCmd.BuiltTool("sbox").
+		sboxCmd.builtToolWithoutDeps("sbox").
 			Flag("--sandbox-path").Text(shared.TempDirForOutDir(PathForOutput(r.ctx).String())).
 			Flag("--manifest").Input(r.sboxManifestPath)
 
@@ -839,14 +839,6 @@
 		// The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
 		return filepath.Join(sboxToolsSubDir, "out", relOutSoong)
 	}
-	if ctx.Config().KatiEnabled() {
-		toolDir = toolDir.ToMakePath()
-		relOut, isRelOut, _ := maybeRelErr(toolDir.String(), path.String())
-		if isRelOut {
-			// The tool is in the Make output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
-			return filepath.Join(sboxToolsSubDir, "out", relOut)
-		}
-	}
 	// The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
 	return filepath.Join(sboxToolsSubDir, "src", path.String())
 }
@@ -1048,6 +1040,19 @@
 // It is equivalent to:
 //  cmd.Tool(ctx.Config().HostToolPath(ctx, tool))
 func (c *RuleBuilderCommand) BuiltTool(tool string) *RuleBuilderCommand {
+	if c.rule.ctx.Config().UseHostMusl() {
+		// If the host is using musl, assume that the tool was built against musl libc and include
+		// libc_musl.so in the sandbox.
+		// TODO(ccross): if we supported adding new dependencies during GenerateAndroidBuildActions
+		// this could be a dependency + TransitivePackagingSpecs.
+		c.ImplicitTool(c.rule.ctx.Config().HostJNIToolPath(c.rule.ctx, "libc_musl"))
+	}
+	return c.builtToolWithoutDeps(tool)
+}
+
+// builtToolWithoutDeps is similar to BuiltTool, but doesn't add any dependencies.  It is used
+// internally by RuleBuilder for helper tools that are known to be compiled statically.
+func (c *RuleBuilderCommand) builtToolWithoutDeps(tool string) *RuleBuilderCommand {
 	return c.Tool(c.rule.ctx.Config().HostToolPath(c.rule.ctx, tool))
 }
 
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 91bbce6..bd73645 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -378,6 +378,7 @@
 			ctx.PropertyErrorf("from", "failed to open %q: %s", from, err)
 			return (map[string]blueprint.ModuleFactory)(nil)
 		}
+		defer r.Close()
 
 		mtDef, errs := soongconfig.Parse(r, from)
 		if ctx.Config().runningAsBp2Build {
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index acb9d18..ceb8e45 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -386,6 +386,46 @@
 	})).RunTest(t)
 }
 
+func TestDuplicateStringValueInSoongConfigStringVariable(t *testing.T) {
+	bp := `
+		soong_config_string_variable {
+			name: "board",
+			values: ["soc_a", "soc_b", "soc_c", "soc_a"],
+		}
+
+		soong_config_module_type {
+			name: "acme_test",
+			module_type: "test",
+			config_namespace: "acme",
+			variables: ["board"],
+			properties: ["cflags", "srcs", "defaults"],
+		}
+    `
+
+	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
+		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.VendorVars = vars
+		})
+	}
+
+	GroupFixturePreparers(
+		fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
+		PrepareForTestWithDefaults,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("soong_config_module_type_import", SoongConfigModuleTypeImportFactory)
+			ctx.RegisterModuleType("soong_config_module_type", SoongConfigModuleTypeFactory)
+			ctx.RegisterModuleType("soong_config_string_variable", SoongConfigStringVariableDummyFactory)
+			ctx.RegisterModuleType("soong_config_bool_variable", SoongConfigBoolVariableDummyFactory)
+			ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
+			ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
+		}),
+		FixtureWithRootAndroidBp(bp),
+	).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
+		// TODO(b/171232169): improve the error message for non-existent properties
+		`Android.bp: soong_config_string_variable: values property error: duplicate value: "soc_a"`,
+	})).RunTest(t)
+}
+
 func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
 	config := TestConfig(buildDir, nil, bp, fs)
 
diff --git a/android/soongconfig/Android.bp b/android/soongconfig/Android.bp
index 9bf3344..8fe1ff1 100644
--- a/android/soongconfig/Android.bp
+++ b/android/soongconfig/Android.bp
@@ -10,6 +10,7 @@
         "blueprint-parser",
         "blueprint-proptools",
         "soong-bazel",
+        "soong-starlark-format",
     ],
     srcs: [
         "config.go",
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 09a5057..212b752 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -25,6 +25,8 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/parser"
 	"github.com/google/blueprint/proptools"
+
+	"android/soong/starlark_fmt"
 )
 
 const conditionsDefault = "conditions_default"
@@ -177,10 +179,14 @@
 		return []error{fmt.Errorf("values property must be set")}
 	}
 
+	vals := make(map[string]bool, len(stringProps.Values))
 	for _, name := range stringProps.Values {
 		if err := checkVariableName(name); err != nil {
 			return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
+		} else if _, ok := vals[name]; ok {
+			return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
 		}
+		vals[name] = true
 	}
 
 	v.variables[base.variable] = &stringVariable{
@@ -235,7 +241,12 @@
 // string vars, bool vars and value vars created by every
 // soong_config_module_type in this build.
 type Bp2BuildSoongConfigDefinitions struct {
-	StringVars map[string]map[string]bool
+	// varCache contains a cache of string variables namespace + property
+	// The same variable may be used in multiple module types (for example, if need support
+	// for cc_default and java_default), only need to process once
+	varCache map[string]bool
+
+	StringVars map[string][]string
 	BoolVars   map[string]bool
 	ValueVars  map[string]bool
 }
@@ -253,7 +264,7 @@
 	defer bp2buildSoongConfigVarsLock.Unlock()
 
 	if defs.StringVars == nil {
-		defs.StringVars = make(map[string]map[string]bool)
+		defs.StringVars = make(map[string][]string)
 	}
 	if defs.BoolVars == nil {
 		defs.BoolVars = make(map[string]bool)
@@ -261,15 +272,24 @@
 	if defs.ValueVars == nil {
 		defs.ValueVars = make(map[string]bool)
 	}
+	if defs.varCache == nil {
+		defs.varCache = make(map[string]bool)
+	}
 	for _, moduleType := range mtDef.ModuleTypes {
 		for _, v := range moduleType.Variables {
 			key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
+
+			// The same variable may be used in multiple module types (for example, if need support
+			// for cc_default and java_default), only need to process once
+			if _, keyInCache := defs.varCache[key]; keyInCache {
+				continue
+			} else {
+				defs.varCache[key] = true
+			}
+
 			if strVar, ok := v.(*stringVariable); ok {
-				if _, ok := defs.StringVars[key]; !ok {
-					defs.StringVars[key] = make(map[string]bool, 0)
-				}
 				for _, value := range strVar.values {
-					defs.StringVars[key][value] = true
+					defs.StringVars[key] = append(defs.StringVars[key], value)
 				}
 			} else if _, ok := v.(*boolVariable); ok {
 				defs.BoolVars[key] = true
@@ -302,29 +322,16 @@
 // String emits the Soong config variable definitions as Starlark dictionaries.
 func (defs Bp2BuildSoongConfigDefinitions) String() string {
 	ret := ""
-	ret += "soong_config_bool_variables = {\n"
-	for _, boolVar := range sortedStringKeys(defs.BoolVars) {
-		ret += fmt.Sprintf("    \"%s\": True,\n", boolVar)
-	}
-	ret += "}\n"
-	ret += "\n"
+	ret += "soong_config_bool_variables = "
+	ret += starlark_fmt.PrintBoolDict(defs.BoolVars, 0)
+	ret += "\n\n"
 
-	ret += "soong_config_value_variables = {\n"
-	for _, valueVar := range sortedStringKeys(defs.ValueVars) {
-		ret += fmt.Sprintf("    \"%s\": True,\n", valueVar)
-	}
-	ret += "}\n"
-	ret += "\n"
+	ret += "soong_config_value_variables = "
+	ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
+	ret += "\n\n"
 
-	ret += "soong_config_string_variables = {\n"
-	for _, stringVar := range sortedStringKeys(defs.StringVars) {
-		ret += fmt.Sprintf("    \"%s\": [\n", stringVar)
-		for _, choice := range sortedStringKeys(defs.StringVars[stringVar]) {
-			ret += fmt.Sprintf("        \"%s\",\n", choice)
-		}
-		ret += fmt.Sprintf("    ],\n")
-	}
-	ret += "}"
+	ret += "soong_config_string_variables = "
+	ret += starlark_fmt.PrintStringListDict(defs.StringVars, 0)
 
 	return ret
 }
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index b14f8b4..a7800e8 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -367,19 +367,19 @@
 
 func Test_Bp2BuildSoongConfigDefinitions(t *testing.T) {
 	testCases := []struct {
+		desc     string
 		defs     Bp2BuildSoongConfigDefinitions
 		expected string
 	}{
 		{
+			desc: "all empty",
 			defs: Bp2BuildSoongConfigDefinitions{},
-			expected: `soong_config_bool_variables = {
-}
+			expected: `soong_config_bool_variables = {}
 
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
 
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+			desc: "only bool",
 			defs: Bp2BuildSoongConfigDefinitions{
 				BoolVars: map[string]bool{
 					"bool_var": true,
@@ -389,39 +389,35 @@
     "bool_var": True,
 }
 
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
 
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+			desc: "only value vars",
 			defs: Bp2BuildSoongConfigDefinitions{
 				ValueVars: map[string]bool{
 					"value_var": true,
 				},
 			},
-			expected: `soong_config_bool_variables = {
-}
+			expected: `soong_config_bool_variables = {}
 
 soong_config_value_variables = {
     "value_var": True,
 }
 
-soong_config_string_variables = {
-}`}, {
+soong_config_string_variables = {}`}, {
+			desc: "only string vars",
 			defs: Bp2BuildSoongConfigDefinitions{
-				StringVars: map[string]map[string]bool{
-					"string_var": map[string]bool{
-						"choice1": true,
-						"choice2": true,
-						"choice3": true,
+				StringVars: map[string][]string{
+					"string_var": []string{
+						"choice1",
+						"choice2",
+						"choice3",
 					},
 				},
 			},
-			expected: `soong_config_bool_variables = {
-}
+			expected: `soong_config_bool_variables = {}
 
-soong_config_value_variables = {
-}
+soong_config_value_variables = {}
 
 soong_config_string_variables = {
     "string_var": [
@@ -430,6 +426,7 @@
         "choice3",
     ],
 }`}, {
+			desc: "all vars",
 			defs: Bp2BuildSoongConfigDefinitions{
 				BoolVars: map[string]bool{
 					"bool_var_one": true,
@@ -438,15 +435,15 @@
 					"value_var_one": true,
 					"value_var_two": true,
 				},
-				StringVars: map[string]map[string]bool{
-					"string_var_one": map[string]bool{
-						"choice1": true,
-						"choice2": true,
-						"choice3": true,
+				StringVars: map[string][]string{
+					"string_var_one": []string{
+						"choice1",
+						"choice2",
+						"choice3",
 					},
-					"string_var_two": map[string]bool{
-						"foo": true,
-						"bar": true,
+					"string_var_two": []string{
+						"foo",
+						"bar",
 					},
 				},
 			},
@@ -466,15 +463,17 @@
         "choice3",
     ],
     "string_var_two": [
-        "bar",
         "foo",
+        "bar",
     ],
 }`},
 	}
 	for _, test := range testCases {
-		actual := test.defs.String()
-		if actual != test.expected {
-			t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
-		}
+		t.Run(test.desc, func(t *testing.T) {
+			actual := test.defs.String()
+			if actual != test.expected {
+				t.Errorf("Expected:\n%s\nbut got:\n%s", test.expected, actual)
+			}
+		})
 	}
 }
diff --git a/android/test_suites.go b/android/test_suites.go
index 22f6cf2..55e1da7 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -60,7 +60,7 @@
 	for _, module := range SortedStringKeys(files) {
 		installedPaths = append(installedPaths, files[module]...)
 	}
-	testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases", false).ToMakePath()
+	testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases", false)
 
 	outputFile := PathForOutput(ctx, "packaging", "robolectric-tests.zip")
 	rule := NewRuleBuilder(pctx, ctx)
diff --git a/android/testing.go b/android/testing.go
index 6290d43..39864e1 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -208,16 +208,6 @@
 	ctx.config.bp2buildPackageConfig = config
 }
 
-// RegisterBp2BuildMutator registers a BazelTargetModule mutator for converting a module
-// type to the equivalent Bazel target.
-func (ctx *TestContext) RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
-	f := func(ctx RegisterMutatorsContext) {
-		ctx.TopDown(moduleType, m)
-	}
-	ctx.config.bp2buildModuleTypeConfig[moduleType] = true
-	ctx.bp2buildMutators = append(ctx.bp2buildMutators, f)
-}
-
 // PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
 // into Bazel BUILD targets that should run prior to deps and conversion.
 func (ctx *TestContext) PreArchBp2BuildMutators(f RegisterMutatorFunc) {
@@ -459,7 +449,7 @@
 // RegisterForBazelConversion prepares a test context for bp2build conversion.
 func (ctx *TestContext) RegisterForBazelConversion() {
 	ctx.SetRunningAsBp2build()
-	RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch, ctx.bp2buildMutators)
+	RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch)
 }
 
 func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
@@ -791,19 +781,21 @@
 	return p
 }
 
-func (b baseTestingComponent) maybeBuildParamsFromDescription(desc string) TestingBuildParams {
+func (b baseTestingComponent) maybeBuildParamsFromDescription(desc string) (TestingBuildParams, []string) {
+	var searchedDescriptions []string
 	for _, p := range b.provider.BuildParamsForTests() {
+		searchedDescriptions = append(searchedDescriptions, p.Description)
 		if strings.Contains(p.Description, desc) {
-			return b.newTestingBuildParams(p)
+			return b.newTestingBuildParams(p), searchedDescriptions
 		}
 	}
-	return TestingBuildParams{}
+	return TestingBuildParams{}, searchedDescriptions
 }
 
 func (b baseTestingComponent) buildParamsFromDescription(desc string) TestingBuildParams {
-	p := b.maybeBuildParamsFromDescription(desc)
+	p, searchedDescriptions := b.maybeBuildParamsFromDescription(desc)
 	if p.Rule == nil {
-		panic(fmt.Errorf("couldn't find description %q", desc))
+		panic(fmt.Errorf("couldn't find description %q\nall descriptions:\n%s", desc, strings.Join(searchedDescriptions, "\n")))
 	}
 	return p
 }
@@ -870,7 +862,8 @@
 // MaybeDescription finds a call to ctx.Build with BuildParams.Description set to a the given string.  Returns an empty
 // BuildParams if no rule is found.
 func (b baseTestingComponent) MaybeDescription(desc string) TestingBuildParams {
-	return b.maybeBuildParamsFromDescription(desc)
+	p, _ := b.maybeBuildParamsFromDescription(desc)
+	return p
 }
 
 // Description finds a call to ctx.Build with BuildParams.Description set to a the given string.  Panics if no rule is
diff --git a/android/util.go b/android/util.go
index a0394f6..47c4583 100644
--- a/android/util.go
+++ b/android/util.go
@@ -65,52 +65,55 @@
 	return buf.String()
 }
 
-// SortedIntKeys returns the keys of the given integer-keyed map in the ascending order
-// TODO(asmundak): once Go has generics, combine this with SortedStringKeys below.
-func SortedIntKeys(m interface{}) []int {
-	v := reflect.ValueOf(m)
-	if v.Kind() != reflect.Map {
-		panic(fmt.Sprintf("%#v is not a map", m))
-	}
-	keys := v.MapKeys()
-	s := make([]int, 0, len(keys))
-	for _, key := range keys {
-		s = append(s, int(key.Int()))
-	}
-	sort.Ints(s)
-	return s
-}
-
-// SorterStringKeys returns the keys of the given string-keyed map in the ascending order
+// SorterStringKeys returns the keys of the given string-keyed map in the ascending order.
 func SortedStringKeys(m interface{}) []string {
 	v := reflect.ValueOf(m)
 	if v.Kind() != reflect.Map {
 		panic(fmt.Sprintf("%#v is not a map", m))
 	}
-	keys := v.MapKeys()
-	s := make([]string, 0, len(keys))
-	for _, key := range keys {
-		s = append(s, key.String())
+	if v.Len() == 0 {
+		return nil
+	}
+	iter := v.MapRange()
+	s := make([]string, 0, v.Len())
+	for iter.Next() {
+		s = append(s, iter.Key().String())
 	}
 	sort.Strings(s)
 	return s
 }
 
-// SortedStringMapValues returns the values of the string-values map in the ascending order
-func SortedStringMapValues(m interface{}) []string {
+// stringValues returns the values of the given string-valued map in randomized map order.
+func stringValues(m interface{}) []string {
 	v := reflect.ValueOf(m)
 	if v.Kind() != reflect.Map {
 		panic(fmt.Sprintf("%#v is not a map", m))
 	}
-	keys := v.MapKeys()
-	s := make([]string, 0, len(keys))
-	for _, key := range keys {
-		s = append(s, v.MapIndex(key).String())
+	if v.Len() == 0 {
+		return nil
 	}
+	iter := v.MapRange()
+	s := make([]string, 0, v.Len())
+	for iter.Next() {
+		s = append(s, iter.Value().String())
+	}
+	return s
+}
+
+// SortedStringValues returns the values of the given string-valued map in the ascending order.
+func SortedStringValues(m interface{}) []string {
+	s := stringValues(m)
 	sort.Strings(s)
 	return s
 }
 
+// SortedUniqueStringValues returns the values of the given string-valued map in the ascending order
+// with duplicates removed.
+func SortedUniqueStringValues(m interface{}) []string {
+	s := stringValues(m)
+	return SortedUniqueStrings(s)
+}
+
 // IndexList returns the index of the first occurrence of the given string in the list or -1
 func IndexList(s string, list []string) int {
 	for i, l := range list {
diff --git a/android/util_test.go b/android/util_test.go
index 09bec01..9b9253b 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -640,3 +640,117 @@
 		})
 	}
 }
+
+func TestSortedStringKeys(t *testing.T) {
+	testCases := []struct {
+		name     string
+		in       interface{}
+		expected []string
+	}{
+		{
+			name:     "nil",
+			in:       map[string]string(nil),
+			expected: nil,
+		},
+		{
+			name:     "empty",
+			in:       map[string]string{},
+			expected: nil,
+		},
+		{
+			name:     "simple",
+			in:       map[string]string{"a": "foo", "b": "bar"},
+			expected: []string{"a", "b"},
+		},
+		{
+			name:     "interface values",
+			in:       map[string]interface{}{"a": nil, "b": nil},
+			expected: []string{"a", "b"},
+		},
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			got := SortedStringKeys(tt.in)
+			if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+				t.Errorf("wanted %q, got %q", w, g)
+			}
+		})
+	}
+}
+
+func TestSortedStringValues(t *testing.T) {
+	testCases := []struct {
+		name     string
+		in       interface{}
+		expected []string
+	}{
+		{
+			name:     "nil",
+			in:       map[string]string(nil),
+			expected: nil,
+		},
+		{
+			name:     "empty",
+			in:       map[string]string{},
+			expected: nil,
+		},
+		{
+			name:     "simple",
+			in:       map[string]string{"foo": "a", "bar": "b"},
+			expected: []string{"a", "b"},
+		},
+		{
+			name:     "duplicates",
+			in:       map[string]string{"foo": "a", "bar": "b", "baz": "b"},
+			expected: []string{"a", "b", "b"},
+		},
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			got := SortedStringValues(tt.in)
+			if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+				t.Errorf("wanted %q, got %q", w, g)
+			}
+		})
+	}
+}
+
+func TestSortedUniqueStringValues(t *testing.T) {
+	testCases := []struct {
+		name     string
+		in       interface{}
+		expected []string
+	}{
+		{
+			name:     "nil",
+			in:       map[string]string(nil),
+			expected: nil,
+		},
+		{
+			name:     "empty",
+			in:       map[string]string{},
+			expected: nil,
+		},
+		{
+			name:     "simple",
+			in:       map[string]string{"foo": "a", "bar": "b"},
+			expected: []string{"a", "b"},
+		},
+		{
+			name:     "duplicates",
+			in:       map[string]string{"foo": "a", "bar": "b", "baz": "b"},
+			expected: []string{"a", "b"},
+		},
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			got := SortedUniqueStringValues(tt.in)
+			if g, w := got, tt.expected; !reflect.DeepEqual(g, w) {
+				t.Errorf("wanted %q, got %q", w, g)
+			}
+		})
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index a29c6f8..59ae919 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -124,9 +124,12 @@
 			Shared_libs       []string
 
 			Cmdline []string
+
+			Srcs         []string
+			Exclude_srcs []string
 		}
 
-		// eng is true for -eng builds, and can be used to turn on additionaly heavyweight debugging
+		// eng is true for -eng builds, and can be used to turn on additional heavyweight debugging
 		// features.
 		Eng struct {
 			Cflags   []string
@@ -299,8 +302,6 @@
 	ClangTidy  *bool   `json:",omitempty"`
 	TidyChecks *string `json:",omitempty"`
 
-	SamplingPGO *bool `json:",omitempty"`
-
 	JavaCoveragePaths        []string `json:",omitempty"`
 	JavaCoverageExcludePaths []string `json:",omitempty"`
 
@@ -329,7 +330,8 @@
 
 	NamespacesToExport []string `json:",omitempty"`
 
-	PgoAdditionalProfileDirs []string `json:",omitempty"`
+	AfdoAdditionalProfileDirs []string `json:",omitempty"`
+	PgoAdditionalProfileDirs  []string `json:",omitempty"`
 
 	VndkUseCoreVariant         *bool `json:",omitempty"`
 	VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
@@ -346,17 +348,27 @@
 	RecoverySnapshotDirsIncluded []string `json:",omitempty"`
 	HostFakeSnapshotEnabled      bool     `json:",omitempty"`
 
-	BoardVendorSepolicyDirs      []string `json:",omitempty"`
-	BoardOdmSepolicyDirs         []string `json:",omitempty"`
-	BoardReqdMaskPolicy          []string `json:",omitempty"`
-	SystemExtPublicSepolicyDirs  []string `json:",omitempty"`
-	SystemExtPrivateSepolicyDirs []string `json:",omitempty"`
-	BoardSepolicyM4Defs          []string `json:",omitempty"`
+	BoardVendorSepolicyDirs           []string `json:",omitempty"`
+	BoardOdmSepolicyDirs              []string `json:",omitempty"`
+	BoardReqdMaskPolicy               []string `json:",omitempty"`
+	BoardPlatVendorPolicy             []string `json:",omitempty"`
+	BoardSystemExtPublicPrebuiltDirs  []string `json:",omitempty"`
+	BoardSystemExtPrivatePrebuiltDirs []string `json:",omitempty"`
+	BoardProductPublicPrebuiltDirs    []string `json:",omitempty"`
+	BoardProductPrivatePrebuiltDirs   []string `json:",omitempty"`
+	SystemExtPublicSepolicyDirs       []string `json:",omitempty"`
+	SystemExtPrivateSepolicyDirs      []string `json:",omitempty"`
+	BoardSepolicyM4Defs               []string `json:",omitempty"`
 
 	BoardSepolicyVers       *string `json:",omitempty"`
 	PlatformSepolicyVersion *string `json:",omitempty"`
 	TotSepolicyVersion      *string `json:",omitempty"`
 
+	SystemExtSepolicyPrebuiltApiDir *string `json:",omitempty"`
+	ProductSepolicyPrebuiltApiDir   *string `json:",omitempty"`
+
+	PlatformSepolicyCompatVersions []string `json:",omitempty"`
+
 	VendorVars map[string]map[string]string `json:",omitempty"`
 
 	Ndk_abis *bool `json:",omitempty"`
@@ -592,10 +604,16 @@
 
 	value := p.FullConfig
 	if value == p.Name {
-		value = "enabled"
+		value = ""
 	}
-	// e.g. acme__feature1__enabled, android__board__soc_a
-	return strings.ToLower(strings.Join([]string{p.Namespace, p.Name, value}, "__"))
+
+	// e.g. acme__feature1, android__board__soc_a
+	selectKey := strings.ToLower(strings.Join([]string{p.Namespace, p.Name}, "__"))
+	if value != "" {
+		selectKey = strings.ToLower(strings.Join([]string{selectKey, value}, "__"))
+	}
+
+	return selectKey
 }
 
 // ProductConfigProperties is a map of maps to group property values according
@@ -1022,7 +1040,7 @@
 
 	printfIntoProperties(ctx, prefix, productVariablePropertyValue, variableValue)
 
-	err := proptools.AppendMatchingProperties(m.generalProperties,
+	err := proptools.AppendMatchingProperties(m.GetProperties(),
 		productVariablePropertyValue.Addr().Interface(), nil)
 	if err != nil {
 		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index d64eb7a..f050a2e 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -122,17 +122,10 @@
 
 	s.CopySpecsToDir(ctx, builder, packageSpecs, dir)
 
-	// Collect licenses to write into NOTICE.txt
-	noticeMap := map[string]android.Paths{}
-	for path, pkgSpec := range packageSpecs {
-		licenseFiles := pkgSpec.EffectiveLicenseFiles()
-		if len(licenseFiles) > 0 {
-			noticeMap[path] = pkgSpec.EffectiveLicenseFiles()
-		}
-	}
-	notices := android.BuildNotices(ctx, noticeMap)
+	noticeFile := android.PathForModuleOut(ctx, "NOTICES.txt")
+	android.BuildNoticeTextOutputFromLicenseMetadata(ctx, noticeFile)
 	builder.Command().Text("cp").
-		Input(notices.TxtOutput.Path()).
+		Input(noticeFile).
 		Text(filepath.Join(dir.String(), "NOTICE.txt"))
 
 	// Handle `merge_zips` by extracting their contents into our tmpdir
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 1045ca6..295b0e5 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -68,6 +68,8 @@
 	"LOCAL_MODULE_PATH":                    prebuiltModulePath,
 	"LOCAL_REPLACE_PREBUILT_APK_INSTALLED": prebuiltPreprocessed,
 
+	"LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG": invert("auto_gen_config"),
+
 	// composite functions
 	"LOCAL_MODULE_TAGS": includeVariableIf(bpVariable{"tags", bpparser.ListType}, not(valueDumpEquals("optional"))),
 
@@ -191,6 +193,7 @@
 			// will be removed later by byfix
 			// TODO: does this property matter in the license module?
 			"LOCAL_LICENSE_CONDITIONS": "android_license_conditions",
+			"LOCAL_GENERATED_SOURCES":  "generated_sources",
 		})
 
 	addStandardProperties(bpparser.BoolType,
@@ -229,6 +232,8 @@
 			"LOCAL_IS_UNIT_TEST": "unit_test",
 
 			"LOCAL_ENFORCE_USES_LIBRARIES": "enforce_uses_libs",
+
+			"LOCAL_CHECK_ELF_FILES": "check_elf_files",
 		})
 }
 
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index ca40aaa..e8b6f78 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -699,7 +699,7 @@
 		expected: `
 			android_library {
 				srcs: ["test.java"],
-				resource_dirs: ["res"],
+
 				jacoco: {
 					include_filter: ["foo.*"],
 				},
@@ -1121,6 +1121,25 @@
 `,
 	},
 	{
+		desc: "prebuilt_root_host",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(HOST_OUT)/subdir
+LOCAL_SRC_FILES := foo.txt
+include $(BUILD_PREBUILT)
+`,
+		expected: `
+prebuilt_root_host {
+	name: "foo",
+
+	src: "foo.txt",
+	relative_install_path: "subdir",
+}
+`,
+	},
+	{
 		desc: "prebuilt_font",
 		in: `
 include $(CLEAR_VARS)
@@ -1439,7 +1458,7 @@
 runtime_resource_overlay {
 	name: "foo",
 	product_specific: true,
-	resource_dirs: ["res"],
+
 	sdk_version: "current",
 	theme: "FooTheme",
 
@@ -1537,9 +1556,11 @@
 	},
 	{
 		desc: "LOCAL_LICENSE_KINDS, LOCAL_LICENSE_CONDITIONS, LOCAL_NOTICE_FILE",
-		// TODO(b/205615944): When valid "android_license_files" exists, the test requires an Android.mk
-		// file (and an Android.bp file is required as well if the license files locates outside the current
-		// directory). So plan to use a mock file system to mock the Android.mk and Android.bp files.
+		// When "android_license_files" is valid, the test requires an Android.mk file
+		// outside the current (and an Android.bp file is required as well if the license
+		// files locates directory), thus a mock file system is needed. The integration
+		// test cases for these scenarios have been added in
+		// $(ANDROID_BUILD_TOP)/build/soong/tests/androidmk_test.sh.
 		in: `
 include $(CLEAR_VARS)
 LOCAL_MODULE := foo
@@ -1564,6 +1585,96 @@
 }
 `,
 	},
+	{
+		desc: "LOCAL_CHECK_ELF_FILES",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_SRC_FILES := test.c
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_CHECK_ELF_FILES := false
+include $(BUILD_PREBUILT)
+		`,
+		expected: `
+cc_prebuilt_library_shared {
+	name: "foo",
+	srcs: ["test.c"],
+
+	check_elf_files: false,
+}
+`,
+	},
+	{
+		desc: "Drop default resource and asset dirs from bp",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+include $(BUILD_PACKAGE)
+`,
+		expected: `
+android_app {
+		name: "foo",
+
+}
+`,
+	},
+	{
+		desc: "LOCAL_GENERATED_SOURCES",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_SRC_FILES := src1, src2, src3
+LOCAL_GENERATED_SOURCES := gen_src1, gen_src2, gen_src3
+include $(BUILD_PACKAGE)
+		`,
+		expected: `
+android_app {
+	name: "foo",
+	srcs: [
+		"src1,",
+		"src2,",
+		"src3",
+	],
+	generated_sources: [
+		"gen_src1,",
+		"gen_src2,",
+		"gen_src3",
+	],
+}
+`,
+	},
+	{
+		desc: "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG is true",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true
+include $(BUILD_PACKAGE)
+		`,
+		expected: `
+android_app {
+	name: "foo",
+	auto_gen_config: false,
+}
+`,
+	},
+	{
+		desc: "LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG is false",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := false
+include $(BUILD_PACKAGE)
+		`,
+		expected: `
+android_app {
+	name: "foo",
+	auto_gen_config: true,
+}
+`,
+	},
 }
 
 func TestEndToEnd(t *testing.T) {
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 7764b6b..8cca137 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -149,7 +149,7 @@
 		var modulePath string
 		if apexType == flattenedApex {
 			// /system/apex/<name>/{lib|framework|...}
-			modulePath = filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.installDir)
+			modulePath = filepath.Join(a.installDir.String(), apexBundleName, fi.installDir)
 			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
 			if a.primaryApexType && !symbolFilesNotNeeded {
 				fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
@@ -309,19 +309,17 @@
 	return moduleNames
 }
 
-func (a *apexBundle) writeRequiredModules(w io.Writer, apexBundleName string) {
+func (a *apexBundle) writeRequiredModules(w io.Writer) {
 	var required []string
 	var targetRequired []string
 	var hostRequired []string
 	required = append(required, a.RequiredModuleNames()...)
 	targetRequired = append(targetRequired, a.TargetRequiredModuleNames()...)
 	hostRequired = append(hostRequired, a.HostRequiredModuleNames()...)
-	installMapSet := make(map[string]bool) // set of dependency module:location mappings
 	for _, fi := range a.filesInfo {
 		required = append(required, fi.requiredModuleNames...)
 		targetRequired = append(targetRequired, fi.targetRequiredModuleNames...)
 		hostRequired = append(hostRequired, fi.hostRequiredModuleNames...)
-		installMapSet[a.fullModuleName(apexBundleName, &fi)+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
 	}
 
 	if len(required) > 0 {
@@ -333,11 +331,6 @@
 	if len(hostRequired) > 0 {
 		fmt.Fprintln(w, "LOCAL_HOST_REQUIRED_MODULES +=", strings.Join(hostRequired, " "))
 	}
-	if len(installMapSet) > 0 {
-		var installs []string
-		installs = append(installs, android.SortedStringKeys(installMapSet)...)
-		fmt.Fprintln(w, "LOCAL_LICENSE_INSTALL_MAP +=", strings.Join(installs, " "))
-	}
 }
 
 func (a *apexBundle) androidMkForType() android.AndroidMkData {
@@ -359,7 +352,7 @@
 				if len(moduleNames) > 0 {
 					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
 				}
-				a.writeRequiredModules(w, name)
+				a.writeRequiredModules(w)
 				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
 
 			} else {
@@ -369,7 +362,7 @@
 				data.Entries.WriteLicenseVariables(w)
 				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
 				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
-				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.ToMakePath().String())
+				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", a.installDir.String())
 				stemSuffix := apexType.suffix()
 				if a.isCompressed {
 					stemSuffix = imageCapexSuffix
@@ -401,7 +394,7 @@
 				if len(a.requiredDeps) > 0 {
 					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
 				}
-				a.writeRequiredModules(w, name)
+				a.writeRequiredModules(w)
 
 				if a.mergedNotices.Merged.Valid() {
 					fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNotices.Merged.Path().String())
diff --git a/apex/apex.go b/apex/apex.go
index 75cff7d..d12a786 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -54,8 +54,6 @@
 	ctx.PreArchMutators(registerPreArchMutators)
 	ctx.PreDepsMutators(RegisterPreDepsMutators)
 	ctx.PostDepsMutators(RegisterPostDepsMutators)
-
-	android.RegisterBp2BuildMutator("apex", ApexBundleBp2Build)
 }
 
 func registerPreArchMutators(ctx android.RegisterMutatorsContext) {
@@ -98,6 +96,14 @@
 	// /system/sepolicy/apex/<module_name>_file_contexts.
 	File_contexts *string `android:"path"`
 
+	// Path to the canned fs config file for customizing file's uid/gid/mod/capabilities. The
+	// format is /<path_or_glob> <uid> <gid> <mode> [capabilities=0x<cap>], where path_or_glob is a
+	// path or glob pattern for a file or set of files, uid/gid are numerial values of user ID
+	// and group ID, mode is octal value for the file mode, and cap is hexadecimal value for the
+	// capability. If this property is not set, or a file is missing in the file, default config
+	// is used.
+	Canned_fs_config *string `android:"path"`
+
 	ApexNativeDependencies
 
 	Multilib apexMultilibProperties
@@ -130,6 +136,13 @@
 	// symlinking to the system libs. Default is true.
 	Updatable *bool
 
+	// Marks that this APEX is designed to be updatable in the future, although it's not
+	// updatable yet. This is used to mimic some of the build behaviors that are applied only to
+	// updatable APEXes. Currently, this disables the size optimization, so that the size of
+	// APEX will not increase when the APEX is actually marked as truly updatable. Default is
+	// false.
+	Future_updatable *bool
+
 	// Whether this APEX can use platform APIs or not. Can be set to true only when `updatable:
 	// false`. Default is false.
 	Platform_apis *bool
@@ -138,12 +151,6 @@
 	// Default: true.
 	Installable *bool
 
-	// Whether this APEX can be compressed or not. Setting this property to false means this
-	// APEX will never be compressed. When set to true, APEX will be compressed if other
-	// conditions, e.g, target device needs to support APEX compression, are also fulfilled.
-	// Default: true.
-	Compressible *bool
-
 	// If set true, VNDK libs are considered as stable libs and are not included in this APEX.
 	// Should be only used in non-system apexes (e.g. vendor: true). Default is false.
 	Use_vndk_as_stable *bool
@@ -334,6 +341,12 @@
 	// certificate and the private key are provided from the android_app_certificate module
 	// named "module".
 	Certificate *string
+
+	// Whether this APEX can be compressed or not. Setting this property to false means this
+	// APEX will never be compressed. When set to true, APEX will be compressed if other
+	// conditions, e.g., target device needs to support APEX compression, are also fulfilled.
+	// Default: false.
+	Compressible *bool
 }
 
 type apexBundle struct {
@@ -445,10 +458,6 @@
 	modulePaths []string
 }
 
-func (*apexBundle) InstallBypassMake() bool {
-	return true
-}
-
 // apexFileClass represents a type of file that can be included in APEX.
 type apexFileClass int
 
@@ -879,9 +888,18 @@
 	// APEX, but shared across APEXes via the VNDK APEX.
 	useVndk := a.SocSpecific() || a.DeviceSpecific() || (a.ProductSpecific() && mctx.Config().EnforceProductPartitionInterface())
 	excludeVndkLibs := useVndk && proptools.Bool(a.properties.Use_vndk_as_stable)
-	if !useVndk && proptools.Bool(a.properties.Use_vndk_as_stable) {
-		mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
-		return
+	if proptools.Bool(a.properties.Use_vndk_as_stable) {
+		if !useVndk {
+			mctx.PropertyErrorf("use_vndk_as_stable", "not supported for system/system_ext APEXes")
+		}
+		mctx.VisitDirectDepsWithTag(sharedLibTag, func(dep android.Module) {
+			if c, ok := dep.(*cc.Module); ok && c.IsVndk() {
+				mctx.PropertyErrorf("use_vndk_as_stable", "Trying to include a VNDK library(%s) while use_vndk_as_stable is true.", dep.Name())
+			}
+		})
+		if mctx.Failed() {
+			return
+		}
 	}
 
 	continueApexDepsWalk := func(child, parent android.Module) bool {
@@ -1301,11 +1319,15 @@
 
 var _ android.ApexBundleDepsInfoIntf = (*apexBundle)(nil)
 
-// Implements android.ApexBudleDepsInfoIntf
+// Implements android.ApexBundleDepsInfoIntf
 func (a *apexBundle) Updatable() bool {
 	return proptools.BoolDefault(a.properties.Updatable, true)
 }
 
+func (a *apexBundle) FutureUpdatable() bool {
+	return proptools.BoolDefault(a.properties.Future_updatable, false)
+}
+
 func (a *apexBundle) UsePlatformApis() bool {
 	return proptools.BoolDefault(a.properties.Platform_apis, false)
 }
@@ -1599,8 +1621,8 @@
 	return af
 }
 
-func apexFileForBpfProgram(ctx android.BaseModuleContext, builtFile android.Path, bpfProgram bpf.BpfModule) apexFile {
-	dirInApex := filepath.Join("etc", "bpf")
+func apexFileForBpfProgram(ctx android.BaseModuleContext, builtFile android.Path, apex_sub_dir string, bpfProgram bpf.BpfModule) apexFile {
+	dirInApex := filepath.Join("etc", "bpf", apex_sub_dir)
 	return newApexFile(ctx, builtFile, builtFile.Base(), dirInApex, etc, bpfProgram)
 }
 
@@ -1670,7 +1692,7 @@
 	// 1) do some validity checks such as apex_available, min_sdk_version, etc.
 	a.checkApexAvailability(ctx)
 	a.checkUpdatable(ctx)
-	a.checkMinSdkVersion(ctx)
+	a.CheckMinSdkVersion(ctx)
 	a.checkStaticLinkingToStubLibraries(ctx)
 	a.checkStaticExecutables(ctx)
 	if len(a.properties.Tests) > 0 && !a.testApex {
@@ -1752,13 +1774,17 @@
 				}
 			case bcpfTag:
 				{
-					if _, ok := child.(*java.BootclasspathFragmentModule); !ok {
+					bcpfModule, ok := child.(*java.BootclasspathFragmentModule)
+					if !ok {
 						ctx.PropertyErrorf("bootclasspath_fragments", "%q is not a bootclasspath_fragment module", depName)
 						return false
 					}
 
 					filesToAdd := apexBootclasspathFragmentFiles(ctx, child)
 					filesInfo = append(filesInfo, filesToAdd...)
+					for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
+						a.requiredDeps = append(a.requiredDeps, makeModuleName)
+					}
 					return true
 				}
 			case sscpfTag:
@@ -1814,8 +1840,9 @@
 			case bpfTag:
 				if bpfProgram, ok := child.(bpf.BpfModule); ok {
 					filesToCopy, _ := bpfProgram.OutputFiles("")
+					apex_sub_dir := bpfProgram.SubDir()
 					for _, bpfFile := range filesToCopy {
-						filesInfo = append(filesInfo, apexFileForBpfProgram(ctx, bpfFile, bpfProgram))
+						filesInfo = append(filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, bpfProgram))
 					}
 				} else {
 					ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
@@ -2004,6 +2031,8 @@
 					}
 				} else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
 					// nothing
+				} else if depTag == android.DarwinUniversalVariantTag {
+					// nothing
 				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
 					ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
 				}
@@ -2105,10 +2134,11 @@
 	}
 
 	forced := ctx.Config().ForceApexSymlinkOptimization()
+	updatable := a.Updatable() || a.FutureUpdatable()
 
 	// We don't need the optimization for updatable APEXes, as it might give false signal
 	// to the system health when the APEXes are still bundled (b/149805758).
-	if !forced && a.Updatable() && a.properties.ApexType == imageApex {
+	if !forced && updatable && a.properties.ApexType == imageApex {
 		a.linkToSystemLib = false
 	}
 
@@ -2159,13 +2189,15 @@
 	var filesToAdd []apexFile
 
 	// Add the boot image files, e.g. .art, .oat and .vdex files.
-	for arch, files := range bootclasspathFragmentInfo.AndroidBootImageFilesByArchType() {
-		dirInApex := filepath.Join("javalib", arch.String())
-		for _, f := range files {
-			androidMkModuleName := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
-			// TODO(b/177892522) - consider passing in the bootclasspath fragment module here instead of nil
-			af := newApexFile(ctx, f, androidMkModuleName, dirInApex, etc, nil)
-			filesToAdd = append(filesToAdd, af)
+	if bootclasspathFragmentInfo.ShouldInstallBootImageInApex() {
+		for arch, files := range bootclasspathFragmentInfo.AndroidBootImageFilesByArchType() {
+			dirInApex := filepath.Join("javalib", arch.String())
+			for _, f := range files {
+				androidMkModuleName := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
+				// TODO(b/177892522) - consider passing in the bootclasspath fragment module here instead of nil
+				af := newApexFile(ctx, f, androidMkModuleName, dirInApex, etc, nil)
+				filesToAdd = append(filesToAdd, af)
+			}
 		}
 	}
 
@@ -2174,6 +2206,40 @@
 		filesToAdd = append(filesToAdd, *af)
 	}
 
+	if pathInApex := bootclasspathFragmentInfo.ProfileInstallPathInApex(); pathInApex != "" {
+		pathOnHost := bootclasspathFragmentInfo.ProfilePathOnHost()
+		tempPath := android.PathForModuleOut(ctx, "boot_image_profile", pathInApex)
+
+		if pathOnHost != nil {
+			// We need to copy the profile to a temporary path with the right filename because the apexer
+			// will take the filename as is.
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.Cp,
+				Input:  pathOnHost,
+				Output: tempPath,
+			})
+		} else {
+			// At this point, the boot image profile cannot be generated. It is probably because the boot
+			// image profile source file does not exist on the branch, or it is not available for the
+			// current build target.
+			// However, we cannot enforce the boot image profile to be generated because some build
+			// targets (such as module SDK) do not need it. It is only needed when the APEX is being
+			// built. Therefore, we create an error rule so that an error will occur at the ninja phase
+			// only if the APEX is being built.
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.ErrorRule,
+				Output: tempPath,
+				Args: map[string]string{
+					"error": "Boot image profile cannot be generated",
+				},
+			})
+		}
+
+		androidMkModuleName := filepath.Base(pathInApex)
+		af := newApexFile(ctx, tempPath, androidMkModuleName, filepath.Dir(pathInApex), etc, nil)
+		filesToAdd = append(filesToAdd, af)
+	}
+
 	return filesToAdd
 }
 
@@ -2300,18 +2366,28 @@
 //
 // TODO(jiyong): move these checks to a separate go file.
 
+var _ android.ModuleWithMinSdkVersionCheck = (*apexBundle)(nil)
+
 // Entures that min_sdk_version of the included modules are equal or less than the min_sdk_version
 // of this apexBundle.
-func (a *apexBundle) checkMinSdkVersion(ctx android.ModuleContext) {
+func (a *apexBundle) CheckMinSdkVersion(ctx android.ModuleContext) {
 	if a.testApex || a.vndkApex {
 		return
 	}
 	// apexBundle::minSdkVersion reports its own errors.
 	minSdkVersion := a.minSdkVersion(ctx)
-	android.CheckMinSdkVersion(a, ctx, minSdkVersion)
+	android.CheckMinSdkVersion(ctx, minSdkVersion, a.WalkPayloadDeps)
 }
 
-func (a *apexBundle) minSdkVersion(ctx android.BaseModuleContext) android.ApiLevel {
+func (a *apexBundle) MinSdkVersion(ctx android.EarlyModuleContext) android.SdkSpec {
+	return android.SdkSpec{
+		Kind:     android.SdkNone,
+		ApiLevel: a.minSdkVersion(ctx),
+		Raw:      String(a.properties.Min_sdk_version),
+	}
+}
+
+func (a *apexBundle) minSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel {
 	ver := proptools.String(a.properties.Min_sdk_version)
 	if ver == "" {
 		return android.NoneApiLevel
@@ -2377,6 +2453,12 @@
 		if a.UsePlatformApis() {
 			ctx.PropertyErrorf("updatable", "updatable APEXes can't use platform APIs")
 		}
+		if a.SocSpecific() || a.DeviceSpecific() {
+			ctx.PropertyErrorf("updatable", "vendor APEXes are not updatable")
+		}
+		if a.FutureUpdatable() {
+			ctx.PropertyErrorf("future_updatable", "Already updatable. Remove `future_updatable: true:`")
+		}
 		a.checkJavaStableSdkVersion(ctx)
 		a.checkClasspathFragments(ctx)
 	}
@@ -2577,7 +2659,7 @@
 	//
 	// Module separator
 	//
-	m["com.android.bluetooth.updatable"] = []string{
+	m["com.android.bluetooth"] = []string{
 		"android.hardware.audio.common@5.0",
 		"android.hardware.bluetooth.a2dp@1.0",
 		"android.hardware.bluetooth.audio@2.0",
@@ -3136,15 +3218,23 @@
 			BootclasspathJar().
 			With("apex_available", module_name).
 			WithMatcher("permitted_packages", android.NotInList(module_packages)).
+			WithMatcher("min_sdk_version", android.LessThanSdkVersion("Tiramisu")).
 			Because("jars that are part of the " + module_name +
-				" module may only allow these packages: " + strings.Join(module_packages, ",") +
-				". Please jarjar or move code around.")
+				" module may only use these package prefixes: " + strings.Join(module_packages, ",") +
+				" with min_sdk < T. Please consider the following alternatives:\n" +
+				"    1. If the offending code is from a statically linked library, consider " +
+				"removing that dependency and using an alternative already in the " +
+				"bootclasspath, or perhaps a shared library." +
+				"    2. Move the offending code into an allowed package.\n" +
+				"    3. Jarjar the offending code. Please be mindful of the potential system " +
+				"health implications of bundling that code, particularly if the offending jar " +
+				"is part of the bootclasspath.")
 		rules = append(rules, permittedPackagesRule)
 	}
 	return rules
 }
 
-// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART.
+// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART on Q/R/S.
 // Adding code to the bootclasspath in new packages will cause issues on module update.
 func qModulesPackages() map[string][]string {
 	return map[string][]string{
@@ -3158,7 +3248,7 @@
 	}
 }
 
-// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART.
+// DO NOT EDIT! These are the package prefixes that are exempted from being AOT'ed by ART on R/S.
 // Adding code to the bootclasspath in new packages will cause issues on module update.
 func rModulesPackages() map[string][]string {
 	return map[string][]string{
@@ -3196,99 +3286,115 @@
 // For Bazel / bp2build
 
 type bazelApexBundleAttributes struct {
-	Manifest           bazel.LabelAttribute
-	Android_manifest   bazel.LabelAttribute
-	File_contexts      bazel.LabelAttribute
-	Key                bazel.LabelAttribute
-	Certificate        bazel.LabelAttribute
-	Min_sdk_version    *string
-	Updatable          bazel.BoolAttribute
-	Installable        bazel.BoolAttribute
-	Native_shared_libs bazel.LabelListAttribute
-	Binaries           bazel.StringListAttribute
-	Prebuilts          bazel.LabelListAttribute
+	Manifest              bazel.LabelAttribute
+	Android_manifest      bazel.LabelAttribute
+	File_contexts         bazel.LabelAttribute
+	Key                   bazel.LabelAttribute
+	Certificate           bazel.LabelAttribute
+	Min_sdk_version       *string
+	Updatable             bazel.BoolAttribute
+	Installable           bazel.BoolAttribute
+	Binaries              bazel.LabelListAttribute
+	Prebuilts             bazel.LabelListAttribute
+	Native_shared_libs_32 bazel.LabelListAttribute
+	Native_shared_libs_64 bazel.LabelListAttribute
+	Compressible          bazel.BoolAttribute
 }
 
-func ApexBundleBp2Build(ctx android.TopDownMutatorContext) {
-	module, ok := ctx.Module().(*apexBundle)
-	if !ok {
-		// Not an APEX bundle
-		return
-	}
-	if !module.ConvertWithBp2build(ctx) {
-		return
-	}
+type convertedNativeSharedLibs struct {
+	Native_shared_libs_32 bazel.LabelListAttribute
+	Native_shared_libs_64 bazel.LabelListAttribute
+}
+
+// ConvertWithBp2build performs bp2build conversion of an apex
+func (a *apexBundle) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	// We do not convert apex_test modules at this time
 	if ctx.ModuleType() != "apex" {
 		return
 	}
 
-	apexBundleBp2BuildInternal(ctx, module)
-}
-
-func apexBundleBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexBundle) {
 	var manifestLabelAttribute bazel.LabelAttribute
-	if module.properties.Manifest != nil {
-		manifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Manifest))
+	if a.properties.Manifest != nil {
+		manifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.Manifest))
 	}
 
 	var androidManifestLabelAttribute bazel.LabelAttribute
-	if module.properties.AndroidManifest != nil {
-		androidManifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.AndroidManifest))
+	if a.properties.AndroidManifest != nil {
+		androidManifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *a.properties.AndroidManifest))
 	}
 
 	var fileContextsLabelAttribute bazel.LabelAttribute
-	if module.properties.File_contexts != nil {
-		fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.properties.File_contexts))
+	if a.properties.File_contexts != nil {
+		fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.properties.File_contexts))
 	}
 
 	var minSdkVersion *string
-	if module.properties.Min_sdk_version != nil {
-		minSdkVersion = module.properties.Min_sdk_version
+	if a.properties.Min_sdk_version != nil {
+		minSdkVersion = a.properties.Min_sdk_version
 	}
 
 	var keyLabelAttribute bazel.LabelAttribute
-	if module.overridableProperties.Key != nil {
-		keyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.overridableProperties.Key))
+	if a.overridableProperties.Key != nil {
+		keyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Key))
 	}
 
 	var certificateLabelAttribute bazel.LabelAttribute
-	if module.overridableProperties.Certificate != nil {
-		certificateLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.overridableProperties.Certificate))
+	if a.overridableProperties.Certificate != nil {
+		certificateLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *a.overridableProperties.Certificate))
 	}
 
-	nativeSharedLibs := module.properties.ApexNativeDependencies.Native_shared_libs
-	nativeSharedLibsLabelList := android.BazelLabelForModuleDeps(ctx, nativeSharedLibs)
-	nativeSharedLibsLabelListAttribute := bazel.MakeLabelListAttribute(nativeSharedLibsLabelList)
+	nativeSharedLibs := &convertedNativeSharedLibs{
+		Native_shared_libs_32: bazel.LabelListAttribute{},
+		Native_shared_libs_64: bazel.LabelListAttribute{},
+	}
+	compileMultilib := "both"
+	if a.CompileMultilib() != nil {
+		compileMultilib = *a.CompileMultilib()
+	}
 
-	prebuilts := module.overridableProperties.Prebuilts
+	// properties.Native_shared_libs is treated as "both"
+	convertBothLibs(ctx, compileMultilib, a.properties.Native_shared_libs, nativeSharedLibs)
+	convertBothLibs(ctx, compileMultilib, a.properties.Multilib.Both.Native_shared_libs, nativeSharedLibs)
+	convert32Libs(ctx, compileMultilib, a.properties.Multilib.Lib32.Native_shared_libs, nativeSharedLibs)
+	convert64Libs(ctx, compileMultilib, a.properties.Multilib.Lib64.Native_shared_libs, nativeSharedLibs)
+	convertFirstLibs(ctx, compileMultilib, a.properties.Multilib.First.Native_shared_libs, nativeSharedLibs)
+
+	prebuilts := a.overridableProperties.Prebuilts
 	prebuiltsLabelList := android.BazelLabelForModuleDeps(ctx, prebuilts)
 	prebuiltsLabelListAttribute := bazel.MakeLabelListAttribute(prebuiltsLabelList)
 
-	binaries := module.properties.ApexNativeDependencies.Binaries
-	binariesStringListAttribute := bazel.MakeStringListAttribute(binaries)
+	binaries := android.BazelLabelForModuleDeps(ctx, a.properties.ApexNativeDependencies.Binaries)
+	binariesLabelListAttribute := bazel.MakeLabelListAttribute(binaries)
 
 	var updatableAttribute bazel.BoolAttribute
-	if module.properties.Updatable != nil {
-		updatableAttribute.Value = module.properties.Updatable
+	if a.properties.Updatable != nil {
+		updatableAttribute.Value = a.properties.Updatable
 	}
 
 	var installableAttribute bazel.BoolAttribute
-	if module.properties.Installable != nil {
-		installableAttribute.Value = module.properties.Installable
+	if a.properties.Installable != nil {
+		installableAttribute.Value = a.properties.Installable
+	}
+
+	var compressibleAttribute bazel.BoolAttribute
+	if a.overridableProperties.Compressible != nil {
+		compressibleAttribute.Value = a.overridableProperties.Compressible
 	}
 
 	attrs := &bazelApexBundleAttributes{
-		Manifest:           manifestLabelAttribute,
-		Android_manifest:   androidManifestLabelAttribute,
-		File_contexts:      fileContextsLabelAttribute,
-		Min_sdk_version:    minSdkVersion,
-		Key:                keyLabelAttribute,
-		Certificate:        certificateLabelAttribute,
-		Updatable:          updatableAttribute,
-		Installable:        installableAttribute,
-		Native_shared_libs: nativeSharedLibsLabelListAttribute,
-		Binaries:           binariesStringListAttribute,
-		Prebuilts:          prebuiltsLabelListAttribute,
+		Manifest:              manifestLabelAttribute,
+		Android_manifest:      androidManifestLabelAttribute,
+		File_contexts:         fileContextsLabelAttribute,
+		Min_sdk_version:       minSdkVersion,
+		Key:                   keyLabelAttribute,
+		Certificate:           certificateLabelAttribute,
+		Updatable:             updatableAttribute,
+		Installable:           installableAttribute,
+		Native_shared_libs_32: nativeSharedLibs.Native_shared_libs_32,
+		Native_shared_libs_64: nativeSharedLibs.Native_shared_libs_64,
+		Binaries:              binariesLabelListAttribute,
+		Prebuilts:             prebuiltsLabelListAttribute,
+		Compressible:          compressibleAttribute,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
@@ -3296,5 +3402,109 @@
 		Bzl_load_location: "//build/bazel/rules:apex.bzl",
 	}
 
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
+}
+
+// The following conversions are based on this table where the rows are the compile_multilib
+// values and the columns are the properties.Multilib.*.Native_shared_libs. Each cell
+// represents how the libs should be compiled for a 64-bit/32-bit device: 32 means it
+// should be compiled as 32-bit, 64 means it should be compiled as 64-bit, none means it
+// should not be compiled.
+// multib/compile_multilib, 32,        64,        both,     first
+// 32,                      32/32,     none/none, 32/32,    none/32
+// 64,                      none/none, 64/none,   64/none,  64/none
+// both,                    32/32,     64/none,   32&64/32, 64/32
+// first,                   32/32,     64/none,   64/32,    64/32
+
+func convert32Libs(ctx android.TopDownMutatorContext, compileMultilb string,
+	libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
+	libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
+	switch compileMultilb {
+	case "both", "32":
+		makeNoConfig32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+	case "first":
+		make32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+	case "64":
+		// Incompatible, ignore
+	default:
+		invalidCompileMultilib(ctx, compileMultilb)
+	}
+}
+
+func convert64Libs(ctx android.TopDownMutatorContext, compileMultilb string,
+	libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
+	libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
+	switch compileMultilb {
+	case "both", "64", "first":
+		make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+	case "32":
+		// Incompatible, ignore
+	default:
+		invalidCompileMultilib(ctx, compileMultilb)
+	}
+}
+
+func convertBothLibs(ctx android.TopDownMutatorContext, compileMultilb string,
+	libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
+	libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
+	switch compileMultilb {
+	case "both":
+		makeNoConfig32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+		make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+	case "first":
+		makeFirstSharedLibsAttributes(libsLabelList, nativeSharedLibs)
+	case "32":
+		makeNoConfig32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+	case "64":
+		make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+	default:
+		invalidCompileMultilib(ctx, compileMultilb)
+	}
+}
+
+func convertFirstLibs(ctx android.TopDownMutatorContext, compileMultilb string,
+	libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
+	libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
+	switch compileMultilb {
+	case "both", "first":
+		makeFirstSharedLibsAttributes(libsLabelList, nativeSharedLibs)
+	case "32":
+		make32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+	case "64":
+		make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+	default:
+		invalidCompileMultilib(ctx, compileMultilb)
+	}
+}
+
+func makeFirstSharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) {
+	make32SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+	make64SharedLibsAttributes(libsLabelList, nativeSharedLibs)
+}
+
+func makeNoConfig32SharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) {
+	list := bazel.LabelListAttribute{}
+	list.SetSelectValue(bazel.NoConfigAxis, "", libsLabelList)
+	nativeSharedLibs.Native_shared_libs_32.Append(list)
+}
+
+func make32SharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) {
+	makeSharedLibsAttributes("x86", libsLabelList, &nativeSharedLibs.Native_shared_libs_32)
+	makeSharedLibsAttributes("arm", libsLabelList, &nativeSharedLibs.Native_shared_libs_32)
+}
+
+func make64SharedLibsAttributes(libsLabelList bazel.LabelList, nativeSharedLibs *convertedNativeSharedLibs) {
+	makeSharedLibsAttributes("x86_64", libsLabelList, &nativeSharedLibs.Native_shared_libs_64)
+	makeSharedLibsAttributes("arm64", libsLabelList, &nativeSharedLibs.Native_shared_libs_64)
+}
+
+func makeSharedLibsAttributes(config string, libsLabelList bazel.LabelList,
+	labelListAttr *bazel.LabelListAttribute) {
+	list := bazel.LabelListAttribute{}
+	list.SetSelectValue(bazel.ArchConfigurationAxis, config, libsLabelList)
+	labelListAttr.Append(list)
+}
+
+func invalidCompileMultilib(ctx android.TopDownMutatorContext, value string) {
+	ctx.PropertyErrorf("compile_multilib", "Invalid value: %s", value)
 }
diff --git a/apex/apex_test.go b/apex/apex_test.go
index e2ca234..6d77b06 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -623,7 +623,7 @@
 			java_libs: ["myjar"],
 			apps: ["AppFoo"],
 			rros: ["rro"],
-			bpfs: ["bpf"],
+			bpfs: ["bpf", "netd_test"],
 			updatable: false,
 		}
 
@@ -676,6 +676,12 @@
 			srcs: ["bpf.c", "bpf2.c"],
 		}
 
+		bpf {
+			name: "netd_test",
+			srcs: ["netd_test.c"],
+			sub_dir: "netd",
+		}
+
 	`)
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
 		"etc/myetc",
@@ -685,6 +691,7 @@
 		"overlay/blue/rro.apk",
 		"etc/bpf/bpf.o",
 		"etc/bpf/bpf2.o",
+		"etc/bpf/netd/netd_test.o",
 	})
 }
 
@@ -2581,22 +2588,21 @@
 	`)
 
 	generateFsRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("generateFsConfig")
-	dirs := strings.Split(generateFsRule.Args["exec_paths"], " ")
+	cmd := generateFsRule.RuleParams.Command
 
 	// Ensure that the subdirectories are all listed
-	ensureListContains(t, dirs, "etc")
-	ensureListContains(t, dirs, "etc/foo")
-	ensureListContains(t, dirs, "etc/foo/bar")
-	ensureListContains(t, dirs, "lib64")
-	ensureListContains(t, dirs, "lib64/foo")
-	ensureListContains(t, dirs, "lib64/foo/bar")
-	ensureListContains(t, dirs, "lib")
-	ensureListContains(t, dirs, "lib/foo")
-	ensureListContains(t, dirs, "lib/foo/bar")
-
-	ensureListContains(t, dirs, "bin")
-	ensureListContains(t, dirs, "bin/foo")
-	ensureListContains(t, dirs, "bin/foo/bar")
+	ensureContains(t, cmd, "/etc ")
+	ensureContains(t, cmd, "/etc/foo ")
+	ensureContains(t, cmd, "/etc/foo/bar ")
+	ensureContains(t, cmd, "/lib64 ")
+	ensureContains(t, cmd, "/lib64/foo ")
+	ensureContains(t, cmd, "/lib64/foo/bar ")
+	ensureContains(t, cmd, "/lib ")
+	ensureContains(t, cmd, "/lib/foo ")
+	ensureContains(t, cmd, "/lib/foo/bar ")
+	ensureContains(t, cmd, "/bin ")
+	ensureContains(t, cmd, "/bin/foo ")
+	ensureContains(t, cmd, "/bin/foo/bar ")
 }
 
 func TestFilesInSubDirWhenNativeBridgeEnabled(t *testing.T) {
@@ -2658,7 +2664,10 @@
 }
 
 func TestVendorApex(t *testing.T) {
-	ctx := testApex(t, `
+	result := android.GroupFixturePreparers(
+		prepareForApexTest,
+		android.FixtureModifyConfig(android.SetKatiEnabledForTests),
+	).RunTestWithBp(t, `
 		apex {
 			name: "myapex",
 			key: "myapex.key",
@@ -2682,29 +2691,55 @@
 		}
 	`)
 
-	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+	ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
 		"bin/mybin",
 		"lib64/libfoo.so",
 		// TODO(b/159195575): Add an option to use VNDK libs from VNDK APEX
 		"lib64/libc++.so",
 	})
 
-	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
+	apexBundle := result.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, result.TestContext, apexBundle)
 	name := apexBundle.BaseModuleName()
 	prefix := "TARGET_"
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
-	androidMk := android.StringRelativeToTop(ctx.Config(), builder.String())
+	androidMk := android.StringRelativeToTop(result.Config, builder.String())
 	installPath := "out/target/product/test_device/vendor/apex"
 	ensureContains(t, androidMk, "LOCAL_MODULE_PATH := "+installPath)
 
-	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
+	apexManifestRule := result.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
 	requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
 	ensureListNotContains(t, requireNativeLibs, ":vndk")
 }
 
+func TestVendorApex_use_vndk_as_stable_TryingToIncludeVNDKLib(t *testing.T) {
+	testApexError(t, `Trying to include a VNDK library`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["libc++"], // libc++ is a VNDK lib
+			vendor: true,
+			use_vndk_as_stable: true,
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}`)
+}
+
 func TestVendorApex_use_vndk_as_stable(t *testing.T) {
+	//   myapex                  myapex2
+	//    |                       |
+	//  mybin ------.           mybin2
+	//   \           \          /  |
+	// (stable)   .---\--------`   |
+	//     \     /     \           |
+	//      \   /       \         /
+	//      libvndk       libvendor
+	//      (vndk)
 	ctx := testApex(t, `
 		apex {
 			name: "myapex",
@@ -2735,28 +2770,95 @@
 		cc_library {
 			name: "libvendor",
 			vendor: true,
+			stl: "none",
+		}
+		apex {
+			name: "myapex2",
+			key: "myapex.key",
+			binaries: ["mybin2"],
+			vendor: true,
+			use_vndk_as_stable: false,
+			updatable: false,
+		}
+		cc_binary {
+			name: "mybin2",
+			vendor: true,
+			shared_libs: ["libvndk", "libvendor"],
 		}
 	`)
 
 	vendorVariant := "android_vendor.29_arm64_armv8-a"
 
-	ldRule := ctx.ModuleForTests("mybin", vendorVariant+"_apex10000").Rule("ld")
-	libs := names(ldRule.Args["libFlags"])
-	// VNDK libs(libvndk/libc++) as they are
-	ensureListContains(t, libs, "out/soong/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so")
-	ensureListContains(t, libs, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"libc++/"+vendorVariant+"_shared/libc++.so")
-	// non-stable Vendor libs as APEX variants
-	ensureListContains(t, libs, "out/soong/.intermediates/libvendor/"+vendorVariant+"_shared_apex10000/libvendor.so")
+	for _, tc := range []struct {
+		name                 string
+		apexName             string
+		moduleName           string
+		moduleVariant        string
+		libs                 []string
+		contents             []string
+		requireVndkNamespace bool
+	}{
+		{
+			name:          "use_vndk_as_stable",
+			apexName:      "myapex",
+			moduleName:    "mybin",
+			moduleVariant: vendorVariant + "_apex10000",
+			libs: []string{
+				// should link with vendor variants of VNDK libs(libvndk/libc++)
+				"out/soong/.intermediates/libvndk/" + vendorVariant + "_shared/libvndk.so",
+				"out/soong/.intermediates/" + cc.DefaultCcCommonTestModulesDir + "libc++/" + vendorVariant + "_shared/libc++.so",
+				// unstable Vendor libs as APEX variant
+				"out/soong/.intermediates/libvendor/" + vendorVariant + "_shared_apex10000/libvendor.so",
+			},
+			contents: []string{
+				"bin/mybin",
+				"lib64/libvendor.so",
+				// VNDK libs (libvndk/libc++) are not included
+			},
+			requireVndkNamespace: true,
+		},
+		{
+			name:          "!use_vndk_as_stable",
+			apexName:      "myapex2",
+			moduleName:    "mybin2",
+			moduleVariant: vendorVariant + "_myapex2",
+			libs: []string{
+				// should link with "unique" APEX(myapex2) variant of VNDK libs(libvndk/libc++)
+				"out/soong/.intermediates/libvndk/" + vendorVariant + "_shared_myapex2/libvndk.so",
+				"out/soong/.intermediates/" + cc.DefaultCcCommonTestModulesDir + "libc++/" + vendorVariant + "_shared_myapex2/libc++.so",
+				// unstable vendor libs have "merged" APEX variants
+				"out/soong/.intermediates/libvendor/" + vendorVariant + "_shared_apex10000/libvendor.so",
+			},
+			contents: []string{
+				"bin/mybin2",
+				"lib64/libvendor.so",
+				// VNDK libs are included as well
+				"lib64/libvndk.so",
+				"lib64/libc++.so",
+			},
+			requireVndkNamespace: false,
+		},
+	} {
+		t.Run(tc.name, func(t *testing.T) {
+			// Check linked libs
+			ldRule := ctx.ModuleForTests(tc.moduleName, tc.moduleVariant).Rule("ld")
+			libs := names(ldRule.Args["libFlags"])
+			for _, lib := range tc.libs {
+				ensureListContains(t, libs, lib)
+			}
+			// Check apex contents
+			ensureExactContents(t, ctx, tc.apexName, "android_common_"+tc.apexName+"_image", tc.contents)
 
-	// VNDK libs are not included when use_vndk_as_stable: true
-	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
-		"bin/mybin",
-		"lib64/libvendor.so",
-	})
-
-	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
-	requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
-	ensureListContains(t, requireNativeLibs, ":vndk")
+			// Check "requireNativeLibs"
+			apexManifestRule := ctx.ModuleForTests(tc.apexName, "android_common_"+tc.apexName+"_image").Rule("apexManifestRule")
+			requireNativeLibs := names(apexManifestRule.Args["requireNativeLibs"])
+			if tc.requireVndkNamespace {
+				ensureListContains(t, requireNativeLibs, ":vndk")
+			} else {
+				ensureListNotContains(t, requireNativeLibs, ":vndk")
+			}
+		})
+	}
 }
 
 func TestProductVariant(t *testing.T) {
@@ -2787,7 +2889,7 @@
 	)
 
 	cflags := strings.Fields(
-		ctx.ModuleForTests("foo", "android_product.29_arm64_armv8-a_apex10000").Rule("cc").Args["cFlags"])
+		ctx.ModuleForTests("foo", "android_product.29_arm64_armv8-a_myapex").Rule("cc").Args["cFlags"])
 	ensureListContains(t, cflags, "-D__ANDROID_VNDK__")
 	ensureListContains(t, cflags, "-D__ANDROID_APEX__")
 	ensureListContains(t, cflags, "-D__ANDROID_PRODUCT__")
@@ -7037,6 +7139,7 @@
 				`, insert))
 			}
 		}),
+		dexpreopt.FixtureSetBootImageProfiles("art/build/boot/boot-image-profile.txt"),
 	).
 		ExtendWithErrorHandler(errorHandler).
 		RunTestWithBp(t, bp)
@@ -7044,6 +7147,75 @@
 	return result.TestContext
 }
 
+func TestDuplicateDeapexeresFromPrebuiltApexes(t *testing.T) {
+	preparers := android.GroupFixturePreparers(
+		java.PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithApexBuildComponents,
+	).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			"Multiple installable prebuilt APEXes provide ambiguous deapexers: com.android.myapex and com.mycompany.android.myapex"))
+
+	bpBase := `
+		apex_set {
+			name: "com.android.myapex",
+			installable: true,
+			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+			set: "myapex.apks",
+		}
+
+		apex_set {
+			name: "com.mycompany.android.myapex",
+			apex_name: "com.android.myapex",
+			installable: true,
+			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+			set: "company-myapex.apks",
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "my-bootclasspath-fragment",
+			apex_available: ["com.android.myapex"],
+			%s
+		}
+	`
+
+	t.Run("java_import", func(t *testing.T) {
+		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
+			java_import {
+				name: "libfoo",
+				jars: ["libfoo.jar"],
+				apex_available: ["com.android.myapex"],
+			}
+		`)
+	})
+
+	t.Run("java_sdk_library_import", func(t *testing.T) {
+		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
+			java_sdk_library_import {
+				name: "libfoo",
+				public: {
+					jars: ["libbar.jar"],
+				},
+				apex_available: ["com.android.myapex"],
+			}
+		`)
+	})
+
+	t.Run("prebuilt_bootclasspath_fragment", func(t *testing.T) {
+		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `
+			image_name: "art",
+			contents: ["libfoo"],
+		`)+`
+			java_sdk_library_import {
+				name: "libfoo",
+				public: {
+					jars: ["libbar.jar"],
+				},
+				apex_available: ["com.android.myapex"],
+			}
+		`)
+	})
+}
+
 func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
 	testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, `
 		apex {
@@ -7075,6 +7247,23 @@
 	`)
 }
 
+func TestUpdatable_cannot_be_vendor_apex(t *testing.T) {
+	testApexError(t, `"myapex" .*: updatable: vendor APEXes are not updatable`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: true,
+			soc_specific: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+}
+
 func TestUpdatable_should_not_set_generate_classpaths_proto(t *testing.T) {
 	testApexError(t, `"mysystemserverclasspathfragment" .* it must not set generate_classpaths_proto to false`, `
 		apex {
@@ -7341,6 +7530,7 @@
 					apex_available: ["myapex"],
 					sdk_version: "none",
 					system_modules: "none",
+					min_sdk_version: "30",
 				}
 				java_library {
 					name: "nonbcp_lib2",
@@ -7349,9 +7539,11 @@
 					permitted_packages: ["a.b"],
 					sdk_version: "none",
 					system_modules: "none",
+					min_sdk_version: "30",
 				}
 				apex {
 					name: "myapex",
+					min_sdk_version: "30",
 					key: "myapex.key",
 					java_libs: ["bcp_lib1", "nonbcp_lib2"],
 					updatable: false,
@@ -7364,8 +7556,8 @@
 			},
 		},
 		{
-			name:          "Bootclasspath apex jar not satisfying allowed module packages.",
-			expectedError: `module "bcp_lib2" .* which is restricted because jars that are part of the myapex module may only allow these packages: foo.bar. Please jarjar or move code around.`,
+			name:          "Bootclasspath apex jar not satisfying allowed module packages on Q.",
+			expectedError: `(?s)module "bcp_lib2" .* which is restricted because jars that are part of the myapex module may only use these package prefixes: foo.bar with min_sdk < T. Please consider the following alternatives:\n    1. If the offending code is from a statically linked library, consider removing that dependency and using an alternative already in the bootclasspath, or perhaps a shared library.    2. Move the offending code into an allowed package.\n    3. Jarjar the offending code. Please be mindful of the potential system health implications of bundling that code, particularly if the offending jar is part of the bootclasspath.`,
 			bp: `
 				java_library {
 					name: "bcp_lib1",
@@ -7374,6 +7566,7 @@
 					permitted_packages: ["foo.bar"],
 					sdk_version: "none",
 					system_modules: "none",
+					min_sdk_version: "29",
 				}
 				java_library {
 					name: "bcp_lib2",
@@ -7382,9 +7575,85 @@
 					permitted_packages: ["foo.bar", "bar.baz"],
 					sdk_version: "none",
 					system_modules: "none",
+					min_sdk_version: "29",
 				}
 				apex {
 					name: "myapex",
+					min_sdk_version: "29",
+					key: "myapex.key",
+					java_libs: ["bcp_lib1", "bcp_lib2"],
+					updatable: false,
+				}
+			`,
+			bootJars: []string{"bcp_lib1", "bcp_lib2"},
+			modulesPackages: map[string][]string{
+				"myapex": []string{
+					"foo.bar",
+				},
+			},
+		},
+		{
+			name:          "Bootclasspath apex jar not satisfying allowed module packages on R.",
+			expectedError: `(?s)module "bcp_lib2" .* which is restricted because jars that are part of the myapex module may only use these package prefixes: foo.bar with min_sdk < T. Please consider the following alternatives:\n    1. If the offending code is from a statically linked library, consider removing that dependency and using an alternative already in the bootclasspath, or perhaps a shared library.    2. Move the offending code into an allowed package.\n    3. Jarjar the offending code. Please be mindful of the potential system health implications of bundling that code, particularly if the offending jar is part of the bootclasspath.`,
+			bp: `
+				java_library {
+					name: "bcp_lib1",
+					srcs: ["lib1/src/*.java"],
+					apex_available: ["myapex"],
+					permitted_packages: ["foo.bar"],
+					sdk_version: "none",
+					system_modules: "none",
+					min_sdk_version: "30",
+				}
+				java_library {
+					name: "bcp_lib2",
+					srcs: ["lib2/src/*.java"],
+					apex_available: ["myapex"],
+					permitted_packages: ["foo.bar", "bar.baz"],
+					sdk_version: "none",
+					system_modules: "none",
+					min_sdk_version: "30",
+				}
+				apex {
+					name: "myapex",
+					min_sdk_version: "30",
+					key: "myapex.key",
+					java_libs: ["bcp_lib1", "bcp_lib2"],
+					updatable: false,
+				}
+			`,
+			bootJars: []string{"bcp_lib1", "bcp_lib2"},
+			modulesPackages: map[string][]string{
+				"myapex": []string{
+					"foo.bar",
+				},
+			},
+		},
+		{
+			name:          "Bootclasspath apex jar >= T not satisfying Q/R/S allowed module packages.",
+			expectedError: "",
+			bp: `
+				java_library {
+					name: "bcp_lib1",
+					srcs: ["lib1/src/*.java"],
+					apex_available: ["myapex"],
+					permitted_packages: ["foo.bar"],
+					sdk_version: "none",
+					system_modules: "none",
+					min_sdk_version: "current",
+				}
+				java_library {
+					name: "bcp_lib2",
+					srcs: ["lib2/src/*.java"],
+					apex_available: ["myapex"],
+					permitted_packages: ["foo.bar", "bar.baz"],
+					sdk_version: "none",
+					system_modules: "none",
+					min_sdk_version: "current",
+				}
+				apex {
+					name: "myapex",
+					min_sdk_version: "current",
 					key: "myapex.key",
 					java_libs: ["bcp_lib1", "bcp_lib2"],
 					updatable: false,
@@ -8334,7 +8603,7 @@
 		java_import {
 			name: "foo",
 			jars: ["foo.jar"],
-			installable: true,
+			apex_available: ["myapex"],
 		}
 	`,
 		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
@@ -8392,6 +8661,200 @@
 	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += otherapex")
 }
 
+func TestSdkLibraryCanHaveHigherMinSdkVersion(t *testing.T) {
+	preparer := android.GroupFixturePreparers(
+		PrepareForTestWithApexBuildComponents,
+		prepareForTestWithMyapex,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.PrepareForTestWithJavaDefaultModules,
+		android.PrepareForTestWithAndroidBuildComponents,
+		dexpreopt.FixtureSetApexBootJars("myapex:mybootclasspathlib"),
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:mysystemserverclasspathlib"),
+	)
+
+	// Test java_sdk_library in bootclasspath_fragment may define higher min_sdk_version than the apex
+	t.Run("bootclasspath_fragment jar has higher min_sdk_version than apex", func(t *testing.T) {
+		preparer.RunTestWithBp(t, `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				bootclasspath_fragments: ["mybootclasspathfragment"],
+				min_sdk_version: "30",
+				updatable: false,
+			}
+
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+
+			bootclasspath_fragment {
+				name: "mybootclasspathfragment",
+				contents: ["mybootclasspathlib"],
+				apex_available: ["myapex"],
+			}
+
+			java_sdk_library {
+				name: "mybootclasspathlib",
+				srcs: ["mybootclasspathlib.java"],
+				apex_available: ["myapex"],
+				compile_dex: true,
+				unsafe_ignore_missing_latest_api: true,
+				min_sdk_version: "31",
+				static_libs: ["util"],
+			}
+
+			java_library {
+				name: "util",
+                srcs: ["a.java"],
+				apex_available: ["myapex"],
+				min_sdk_version: "31",
+				static_libs: ["another_util"],
+			}
+
+			java_library {
+				name: "another_util",
+                srcs: ["a.java"],
+				min_sdk_version: "31",
+				apex_available: ["myapex"],
+			}
+		`)
+	})
+
+	// Test java_sdk_library in systemserverclasspath_fragment may define higher min_sdk_version than the apex
+	t.Run("systemserverclasspath_fragment jar has higher min_sdk_version than apex", func(t *testing.T) {
+		preparer.RunTestWithBp(t, `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
+				min_sdk_version: "30",
+				updatable: false,
+			}
+
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+
+			systemserverclasspath_fragment {
+				name: "mysystemserverclasspathfragment",
+				contents: ["mysystemserverclasspathlib"],
+				apex_available: ["myapex"],
+			}
+
+			java_sdk_library {
+				name: "mysystemserverclasspathlib",
+				srcs: ["mysystemserverclasspathlib.java"],
+				apex_available: ["myapex"],
+				compile_dex: true,
+				min_sdk_version: "32",
+				unsafe_ignore_missing_latest_api: true,
+				static_libs: ["util"],
+			}
+
+			java_library {
+				name: "util",
+                srcs: ["a.java"],
+				apex_available: ["myapex"],
+				min_sdk_version: "31",
+				static_libs: ["another_util"],
+			}
+
+			java_library {
+				name: "another_util",
+                srcs: ["a.java"],
+				min_sdk_version: "31",
+				apex_available: ["myapex"],
+			}
+		`)
+	})
+
+	t.Run("bootclasspath_fragment jar must set min_sdk_version", func(t *testing.T) {
+		preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "mybootclasspathlib".*must set min_sdk_version`)).
+			RunTestWithBp(t, `
+				apex {
+					name: "myapex",
+					key: "myapex.key",
+					bootclasspath_fragments: ["mybootclasspathfragment"],
+					min_sdk_version: "30",
+					updatable: false,
+				}
+
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+
+				bootclasspath_fragment {
+					name: "mybootclasspathfragment",
+					contents: ["mybootclasspathlib"],
+					apex_available: ["myapex"],
+				}
+
+				java_sdk_library {
+					name: "mybootclasspathlib",
+					srcs: ["mybootclasspathlib.java"],
+					apex_available: ["myapex"],
+					compile_dex: true,
+					unsafe_ignore_missing_latest_api: true,
+				}
+		`)
+	})
+
+	t.Run("systemserverclasspath_fragment jar must set min_sdk_version", func(t *testing.T) {
+		preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "mysystemserverclasspathlib".*must set min_sdk_version`)).
+			RunTestWithBp(t, `
+				apex {
+					name: "myapex",
+					key: "myapex.key",
+					systemserverclasspath_fragments: ["mysystemserverclasspathfragment"],
+					min_sdk_version: "30",
+					updatable: false,
+				}
+
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+
+				systemserverclasspath_fragment {
+					name: "mysystemserverclasspathfragment",
+					contents: ["mysystemserverclasspathlib"],
+					apex_available: ["myapex"],
+				}
+
+				java_sdk_library {
+					name: "mysystemserverclasspathlib",
+					srcs: ["mysystemserverclasspathlib.java"],
+					apex_available: ["myapex"],
+					compile_dex: true,
+					unsafe_ignore_missing_latest_api: true,
+				}
+		`)
+	})
+}
+
+// Verifies that the APEX depends on all the Make modules in the list.
+func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
+	a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
+	for _, dep := range deps {
+		android.AssertStringListContains(t, "", a.requiredDeps, dep)
+	}
+}
+
+// Verifies that the APEX does not depend on any of the Make modules in the list.
+func ensureDoesNotContainRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
+	a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
+	for _, dep := range deps {
+		android.AssertStringListDoesNotContain(t, "", a.requiredDeps, dep)
+	}
+}
+
 func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index cb7d3d1..ce6b7f7 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -16,11 +16,13 @@
 
 import (
 	"fmt"
+	"path"
 	"sort"
 	"strings"
 	"testing"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 	"android/soong/java"
 
 	"github.com/google/blueprint/proptools"
@@ -35,11 +37,14 @@
 )
 
 // Some additional files needed for the art apex.
-var prepareForTestWithArtApex = android.FixtureMergeMockFs(android.MockFS{
-	"com.android.art.avbpubkey":                          nil,
-	"com.android.art.pem":                                nil,
-	"system/sepolicy/apex/com.android.art-file_contexts": nil,
-})
+var prepareForTestWithArtApex = android.GroupFixturePreparers(
+	android.FixtureMergeMockFs(android.MockFS{
+		"com.android.art.avbpubkey":                          nil,
+		"com.android.art.pem":                                nil,
+		"system/sepolicy/apex/com.android.art-file_contexts": nil,
+	}),
+	dexpreopt.FixtureSetBootImageProfiles("art/build/boot/boot-image-profile.txt"),
+)
 
 func TestBootclasspathFragments(t *testing.T) {
 	result := android.GroupFixturePreparers(
@@ -405,9 +410,11 @@
 			// bootclasspath_fragment's contents property.
 			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
 			addSource("foo", "bar"),
+			java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
 		).RunTest(t)
 
 		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"etc/boot-image.prof",
 			"etc/classpaths/bootclasspath.pb",
 			"javalib/arm/boot.art",
 			"javalib/arm/boot.oat",
@@ -431,12 +438,80 @@
 			`mybootclasspathfragment`,
 		})
 
+		// The boot images are installed in the APEX by Soong, so there shouldn't be any dexpreopt-related Make modules.
+		ensureDoesNotContainRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"mybootclasspathfragment-dexpreopt-arm64-boot.art",
+			"mybootclasspathfragment-dexpreopt-arm64-boot.oat",
+			"mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
+			"mybootclasspathfragment-dexpreopt-arm-boot.art",
+			"mybootclasspathfragment-dexpreopt-arm-boot.oat",
+			"mybootclasspathfragment-dexpreopt-arm-boot.vdex",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
+		})
+
 		// Make sure that the source bootclasspath_fragment copies its dex files to the predefined
 		// locations for the art image.
 		module := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
+	t.Run("boot image files from source no boot image in apex", func(t *testing.T) {
+		result := android.GroupFixturePreparers(
+			commonPreparer,
+
+			// Configure some libraries in the art bootclasspath_fragment that match the source
+			// bootclasspath_fragment's contents property.
+			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+			addSource("foo", "bar"),
+			java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
+		).RunTest(t)
+
+		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"etc/boot-image.prof",
+			"etc/classpaths/bootclasspath.pb",
+			"javalib/bar.jar",
+			"javalib/foo.jar",
+		})
+
+		ensureContainsRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"mybootclasspathfragment-dexpreopt-arm64-boot.art",
+			"mybootclasspathfragment-dexpreopt-arm64-boot.oat",
+			"mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
+			"mybootclasspathfragment-dexpreopt-arm-boot.art",
+			"mybootclasspathfragment-dexpreopt-arm-boot.oat",
+			"mybootclasspathfragment-dexpreopt-arm-boot.vdex",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
+		})
+	})
+
+	t.Run("boot image disable generate profile", func(t *testing.T) {
+		result := android.GroupFixturePreparers(
+			commonPreparer,
+
+			// Configure some libraries in the art bootclasspath_fragment that match the source
+			// bootclasspath_fragment's contents property.
+			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+			addSource("foo", "bar"),
+			dexpreopt.FixtureDisableGenerateProfile(true),
+		).RunTest(t)
+
+		files := getFiles(t, result.TestContext, "com.android.art", "android_common_com.android.art_image")
+		for _, file := range files {
+			matched, _ := path.Match("etc/boot-image.prof", file.path)
+			android.AssertBoolEquals(t, "\"etc/boot-image.prof\" should not be in the APEX", matched, false)
+		}
+	})
+
 	t.Run("boot image files with preferred prebuilt", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
 			commonPreparer,
@@ -448,9 +523,12 @@
 
 			// Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
 			addPrebuilt(true, "foo", "bar"),
+
+			java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
 		).RunTest(t)
 
 		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"etc/boot-image.prof",
 			"etc/classpaths/bootclasspath.pb",
 			"javalib/arm/boot.art",
 			"javalib/arm/boot.oat",
@@ -475,12 +553,66 @@
 			`prebuilt_com.android.art`,
 		})
 
+		// The boot images are installed in the APEX by Soong, so there shouldn't be any dexpreopt-related Make modules.
+		ensureDoesNotContainRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"mybootclasspathfragment-dexpreopt-arm64-boot.art",
+			"mybootclasspathfragment-dexpreopt-arm64-boot.oat",
+			"mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
+			"mybootclasspathfragment-dexpreopt-arm-boot.art",
+			"mybootclasspathfragment-dexpreopt-arm-boot.oat",
+			"mybootclasspathfragment-dexpreopt-arm-boot.vdex",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
+		})
+
 		// Make sure that the prebuilt bootclasspath_fragment copies its dex files to the predefined
 		// locations for the art image.
 		module := result.ModuleForTests("prebuilt_mybootclasspathfragment", "android_common_com.android.art")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
+	t.Run("boot image files from preferred prebuilt no boot image in apex", func(t *testing.T) {
+		result := android.GroupFixturePreparers(
+			commonPreparer,
+
+			// Configure some libraries in the art bootclasspath_fragment that match the source
+			// bootclasspath_fragment's contents property.
+			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+			addSource("foo", "bar"),
+
+			// Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
+			addPrebuilt(true, "foo", "bar"),
+
+			java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
+		).RunTest(t)
+
+		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"etc/boot-image.prof",
+			"etc/classpaths/bootclasspath.pb",
+			"javalib/bar.jar",
+			"javalib/foo.jar",
+		})
+
+		ensureContainsRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"mybootclasspathfragment-dexpreopt-arm64-boot.art",
+			"mybootclasspathfragment-dexpreopt-arm64-boot.oat",
+			"mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
+			"mybootclasspathfragment-dexpreopt-arm-boot.art",
+			"mybootclasspathfragment-dexpreopt-arm-boot.oat",
+			"mybootclasspathfragment-dexpreopt-arm-boot.vdex",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
+		})
+	})
+
 	t.Run("source with inconsistency between config and contents", func(t *testing.T) {
 		android.GroupFixturePreparers(
 			commonPreparer,
@@ -542,7 +674,7 @@
 }
 
 func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) {
-	result := android.GroupFixturePreparers(
+	preparers := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		prepareForTestWithArtApex,
 
@@ -553,7 +685,10 @@
 
 		// Configure some libraries in the art bootclasspath_fragment.
 		java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
-	).RunTestWithBp(t, `
+		java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
+	)
+
+	bp := `
 		prebuilt_apex {
 			name: "com.android.art",
 			arch: {
@@ -599,22 +734,45 @@
 				all_flags: "mybootclasspathfragment/all-flags.csv",
 			},
 		}
-	`)
 
-	java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
-		`com.android.art.apex.selector`,
-		`prebuilt_mybootclasspathfragment`,
+		// A prebuilt apex with the same apex_name that shouldn't interfere when it isn't enabled.
+		prebuilt_apex {
+			name: "com.mycompany.android.art",
+			apex_name: "com.android.art",
+			%s
+			src: "com.mycompany.android.art.apex",
+			exported_bootclasspath_fragments: ["mybootclasspathfragment"],
+		}
+	`
+
+	t.Run("disabled alternative APEX", func(t *testing.T) {
+		result := preparers.RunTestWithBp(t, fmt.Sprintf(bp, "enabled: false,"))
+
+		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
+			`com.android.art.apex.selector`,
+			`prebuilt_mybootclasspathfragment`,
+		})
+
+		java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_com.android.art", []string{
+			`com.android.art.deapexer`,
+			`dex2oatd`,
+			`prebuilt_bar`,
+			`prebuilt_foo`,
+		})
+
+		module := result.ModuleForTests("mybootclasspathfragment", "android_common_com.android.art")
+		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
+
+		// Check that the right deapexer module was chosen for a boot image.
+		param := module.Output("out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art")
+		android.AssertStringDoesContain(t, "didn't find the expected deapexer in the input path", param.Input.String(), "/com.android.art.deapexer")
 	})
 
-	java.CheckModuleDependencies(t, result.TestContext, "mybootclasspathfragment", "android_common_com.android.art", []string{
-		`com.android.art.deapexer`,
-		`dex2oatd`,
-		`prebuilt_bar`,
-		`prebuilt_foo`,
+	t.Run("enabled alternative APEX", func(t *testing.T) {
+		preparers.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			"Multiple installable prebuilt APEXes provide ambiguous deapexers: com.android.art and com.mycompany.android.art")).
+			RunTestWithBp(t, fmt.Sprintf(bp, ""))
 	})
-
-	module := result.ModuleForTests("mybootclasspathfragment", "android_common_com.android.art")
-	checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 }
 
 // checkCopiesToPredefinedLocationForArt checks that the supplied modules are copied to the
diff --git a/apex/builder.go b/apex/builder.go
index 0880e2b..fc4bf8a 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -72,19 +72,6 @@
 }
 
 var (
-	// Create a canned fs config file where all files and directories are
-	// by default set to (uid/gid/mode) = (1000/1000/0644)
-	// TODO(b/113082813) make this configurable using config.fs syntax
-	generateFsConfig = pctx.StaticRule("generateFsConfig", blueprint.RuleParams{
-		Command: `( set -e; echo '/ 1000 1000 0755' ` +
-			`&& for i in ${ro_paths}; do echo "/$$i 1000 1000 0644"; done ` +
-			`&& for i in  ${exec_paths}; do echo "/$$i 0 2000 0755"; done ` +
-			`&& ( tr ' ' '\n' <${out}.apklist | for i in ${apk_paths}; do read apk; echo "/$$i 0 2000 0755"; zipinfo -1 $$apk | sed "s:\(.*\):/$$i/\1 1000 1000 0644:"; done ) ) > ${out}`,
-		Description:    "fs_config ${out}",
-		Rspfile:        "$out.apklist",
-		RspfileContent: "$in",
-	}, "ro_paths", "exec_paths", "apk_paths")
-
 	apexManifestRule = pctx.StaticRule("apexManifestRule", blueprint.RuleParams{
 		Command: `rm -f $out && ${jsonmodify} $in ` +
 			`-a provideNativeLibs ${provideNativeLibs} ` +
@@ -410,6 +397,12 @@
 	return output.OutputPath
 }
 
+func markManifestTestOnly(ctx android.ModuleContext, androidManifestFile android.Path) android.Path {
+	return java.ManifestFixer(ctx, androidManifestFile, java.ManifestFixerParams{
+		TestOnly: true,
+	})
+}
+
 // buildUnflattendApex creates build rules to build an APEX using apexer.
 func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) {
 	apexType := a.properties.ApexType
@@ -434,7 +427,10 @@
 	// Avoid creating duplicate build rules for multi-installed APEXes.
 	if proptools.BoolDefault(a.properties.Multi_install_skip_symbol_files, false) {
 		installSymbolFiles = false
+
 	}
+	// set of dependency module:location mappings
+	installMapSet := make(map[string]bool)
 
 	// TODO(jiyong): use the RuleBuilder
 	var copyCommands []string
@@ -442,7 +438,6 @@
 	pathWhenActivated := android.PathForModuleInPartitionInstall(ctx, "apex", apexName)
 	for _, fi := range a.filesInfo {
 		destPath := imageDir.Join(ctx, fi.path()).String()
-		var installedPath android.InstallPath
 		// Prepare the destination path
 		destPathDir := filepath.Dir(destPath)
 		if fi.class == appSet {
@@ -450,6 +445,8 @@
 		}
 		copyCommands = append(copyCommands, "mkdir -p "+destPathDir)
 
+		installMapPath := fi.builtFile
+
 		// Copy the built file to the directory. But if the symlink optimization is turned
 		// on, place a symlink to the corresponding file in /system partition instead.
 		if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
@@ -457,6 +454,7 @@
 			pathOnDevice := filepath.Join("/system", fi.path())
 			copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
 		} else {
+			var installedPath android.InstallPath
 			if fi.class == appSet {
 				copyCommands = append(copyCommands,
 					fmt.Sprintf("unzip -qDD -d %s %s", destPathDir,
@@ -475,17 +473,19 @@
 			if installSymbolFiles {
 				implicitInputs = append(implicitInputs, installedPath)
 			}
-		}
 
-		// Create additional symlinks pointing the file inside the APEX (if any). Note that
-		// this is independent from the symlink optimization.
-		for _, symlinkPath := range fi.symlinkPaths() {
-			symlinkDest := imageDir.Join(ctx, symlinkPath).String()
-			copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
-			if installSymbolFiles {
-				installedSymlink := ctx.InstallSymlink(pathWhenActivated.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
-				implicitInputs = append(implicitInputs, installedSymlink)
+			// Create additional symlinks pointing the file inside the APEX (if any). Note that
+			// this is independent from the symlink optimization.
+			for _, symlinkPath := range fi.symlinkPaths() {
+				symlinkDest := imageDir.Join(ctx, symlinkPath).String()
+				copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
+				if installSymbolFiles {
+					installedSymlink := ctx.InstallSymlink(pathWhenActivated.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
+					implicitInputs = append(implicitInputs, installedSymlink)
+				}
 			}
+
+			installMapPath = installedPath
 		}
 
 		// Copy the test files (if any)
@@ -502,6 +502,8 @@
 			copyCommands = append(copyCommands, "cp -f "+d.SrcPath.String()+" "+dataDest)
 			implicitInputs = append(implicitInputs, d.SrcPath)
 		}
+
+		installMapSet[installMapPath.String()+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
 	}
 	implicitInputs = append(implicitInputs, a.manifestPbOut)
 	if installSymbolFiles {
@@ -510,6 +512,12 @@
 		implicitInputs = append(implicitInputs, installedManifest, installedKey)
 	}
 
+	if len(installMapSet) > 0 {
+		var installs []string
+		installs = append(installs, android.SortedStringKeys(installMapSet)...)
+		a.SetLicenseInstallMap(installs)
+	}
+
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// Step 1.a: Write the list of files in this APEX to a txt file and compare it against
 	// the allowed list given via the allowed_files property. Build fails when the two lists
@@ -565,58 +573,14 @@
 	outHostBinDir := ctx.Config().HostToolPath(ctx, "").String()
 	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
 
-	// Figure out if need to compress apex.
-	compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.properties.Compressible, false) && !a.testApex && !ctx.Config().UnbundledBuildApps()
+	// Figure out if we need to compress the apex.
+	compressionEnabled := ctx.Config().CompressedApex() && proptools.BoolDefault(a.overridableProperties.Compressible, false) && !a.testApex && !ctx.Config().UnbundledBuildApps()
 	if apexType == imageApex {
+
 		////////////////////////////////////////////////////////////////////////////////////
 		// Step 2: create canned_fs_config which encodes filemode,uid,gid of each files
 		// in this APEX. The file will be used by apexer in later steps.
-		// TODO(jiyong): make this as a function
-		// TODO(jiyong): use the RuleBuilder
-		var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
-		var executablePaths []string // this also includes dirs
-		var extractedAppSetPaths android.Paths
-		var extractedAppSetDirs []string
-		for _, f := range a.filesInfo {
-			pathInApex := f.path()
-			if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
-				executablePaths = append(executablePaths, pathInApex)
-				for _, d := range f.dataPaths {
-					readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, d.RelativeInstallPath, d.SrcPath.Rel()))
-				}
-				for _, s := range f.symlinks {
-					executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
-				}
-			} else if f.class == appSet {
-				extractedAppSetPaths = append(extractedAppSetPaths, f.builtFile)
-				extractedAppSetDirs = append(extractedAppSetDirs, f.installDir)
-			} else {
-				readOnlyPaths = append(readOnlyPaths, pathInApex)
-			}
-			dir := f.installDir
-			for !android.InList(dir, executablePaths) && dir != "" {
-				executablePaths = append(executablePaths, dir)
-				dir, _ = filepath.Split(dir) // move up to the parent
-				if len(dir) > 0 {
-					// remove trailing slash
-					dir = dir[:len(dir)-1]
-				}
-			}
-		}
-		sort.Strings(readOnlyPaths)
-		sort.Strings(executablePaths)
-		cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        generateFsConfig,
-			Output:      cannedFsConfig,
-			Description: "generate fs config",
-			Inputs:      extractedAppSetPaths,
-			Args: map[string]string{
-				"ro_paths":   strings.Join(readOnlyPaths, " "),
-				"exec_paths": strings.Join(executablePaths, " "),
-				"apk_paths":  strings.Join(extractedAppSetDirs, " "),
-			},
-		})
+		cannedFsConfig := a.buildCannedFsConfig(ctx)
 		implicitInputs = append(implicitInputs, cannedFsConfig)
 
 		////////////////////////////////////////////////////////////////////////////////////
@@ -637,8 +601,15 @@
 
 		if a.properties.AndroidManifest != nil {
 			androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest))
+
+			if a.testApex {
+				androidManifestFile = markManifestTestOnly(ctx, androidManifestFile)
+			}
+
 			implicitInputs = append(implicitInputs, androidManifestFile)
 			optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
+		} else if a.testApex {
+			optFlags = append(optFlags, "--test_only")
 		}
 
 		// Determine target/min sdk version from the context
@@ -1057,3 +1028,65 @@
 
 	a.lintReports = java.BuildModuleLintReportZips(ctx, depSetsBuilder.Build())
 }
+
+func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.OutputPath {
+	var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
+	var executablePaths []string // this also includes dirs
+	var appSetDirs []string
+	appSetFiles := make(map[string]android.Path)
+	for _, f := range a.filesInfo {
+		pathInApex := f.path()
+		if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
+			executablePaths = append(executablePaths, pathInApex)
+			for _, d := range f.dataPaths {
+				readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, d.RelativeInstallPath, d.SrcPath.Rel()))
+			}
+			for _, s := range f.symlinks {
+				executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
+			}
+		} else if f.class == appSet {
+			appSetDirs = append(appSetDirs, f.installDir)
+			appSetFiles[f.installDir] = f.builtFile
+		} else {
+			readOnlyPaths = append(readOnlyPaths, pathInApex)
+		}
+		dir := f.installDir
+		for !android.InList(dir, executablePaths) && dir != "" {
+			executablePaths = append(executablePaths, dir)
+			dir, _ = filepath.Split(dir) // move up to the parent
+			if len(dir) > 0 {
+				// remove trailing slash
+				dir = dir[:len(dir)-1]
+			}
+		}
+	}
+	sort.Strings(readOnlyPaths)
+	sort.Strings(executablePaths)
+	sort.Strings(appSetDirs)
+
+	cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config")
+	builder := android.NewRuleBuilder(pctx, ctx)
+	cmd := builder.Command()
+	cmd.Text("(")
+	cmd.Text("echo '/ 1000 1000 0755';")
+	for _, p := range readOnlyPaths {
+		cmd.Textf("echo '/%s 1000 1000 0644';", p)
+	}
+	for _, p := range executablePaths {
+		cmd.Textf("echo '/%s 0 2000 0755';", p)
+	}
+	for _, dir := range appSetDirs {
+		cmd.Textf("echo '/%s 0 2000 0755';", dir)
+		file := appSetFiles[dir]
+		cmd.Text("zipinfo -1").Input(file).Textf(`| sed "s:\(.*\):/%s/\1 1000 1000 0644:";`, dir)
+	}
+	// Custom fs_config is "appended" to the last so that entries from the file are preferred
+	// over default ones set above.
+	if a.properties.Canned_fs_config != nil {
+		cmd.Text("cat").Input(android.PathForModuleSrc(ctx, *a.properties.Canned_fs_config))
+	}
+	cmd.Text(")").FlagWithOutput("> ", cannedFsConfig)
+	builder.Build("generateFsConfig", fmt.Sprintf("Generating canned fs config for %s", a.BaseModuleName()))
+
+	return cannedFsConfig.OutputPath
+}
diff --git a/apex/key.go b/apex/key.go
index 259060f..829410e 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -34,8 +34,6 @@
 func registerApexKeyBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
 	ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
-
-	android.RegisterBp2BuildMutator("apex_key", ApexKeyBp2Build)
 }
 
 type apexKey struct {
@@ -209,20 +207,9 @@
 	Private_key bazel.LabelAttribute
 }
 
-func ApexKeyBp2Build(ctx android.TopDownMutatorContext) {
-	module, ok := ctx.Module().(*apexKey)
-	if !ok {
-		// Not an APEX key
-		return
-	}
-	if !module.ConvertWithBp2build(ctx) {
-		return
-	}
-	if ctx.ModuleType() != "apex_key" {
-		return
-	}
-
-	apexKeyBp2BuildInternal(ctx, module)
+// ConvertWithBp2build performs conversion apexKey for bp2build
+func (m *apexKey) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	apexKeyBp2BuildInternal(ctx, m)
 }
 
 func apexKeyBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexKey) {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 84bdcdd..158c804 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -65,7 +65,8 @@
 	// Installed locations of symlinks for backward compatibility.
 	compatSymlinks android.InstallPaths
 
-	hostRequired []string
+	hostRequired        []string
+	requiredModuleNames []string
 }
 
 type sanitizedPrebuilt interface {
@@ -195,9 +196,19 @@
 				}
 				p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, af)
 			}
-		} else if tag == exportedBootclasspathFragmentTag ||
-			tag == exportedSystemserverclasspathFragmentTag {
-			// Visit the children of the bootclasspath_fragment and systemserver_fragment.
+		} else if tag == exportedBootclasspathFragmentTag {
+			bcpfModule, ok := child.(*java.PrebuiltBootclasspathFragmentModule)
+			if !ok {
+				ctx.PropertyErrorf("exported_bootclasspath_fragments", "%q is not a prebuilt_bootclasspath_fragment module", name)
+				return false
+			}
+			for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
+				p.requiredModuleNames = append(p.requiredModuleNames, makeModuleName)
+			}
+			// Visit the children of the bootclasspath_fragment.
+			return true
+		} else if tag == exportedSystemserverclasspathFragmentTag {
+			// Visit the children of the systemserver_fragment.
 			return true
 		}
 
@@ -211,6 +222,7 @@
 		entries.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", fi.targetRequiredModuleNames...)
 		entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", fi.hostRequiredModuleNames...)
 	}
+	entries.AddStrings("LOCAL_REQUIRED_MODULES", p.requiredModuleNames...)
 }
 
 func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries {
@@ -222,7 +234,7 @@
 			Host_required: p.hostRequired,
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-					entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
+					entries.SetString("LOCAL_MODULE_PATH", p.installDir.String())
 					entries.SetString("LOCAL_MODULE_STEM", p.installFilename)
 					entries.SetPath("LOCAL_SOONG_INSTALLED_MODULE", p.installedFile)
 					entries.SetString("LOCAL_SOONG_INSTALL_PAIRS", p.outputApex.String()+":"+p.installedFile.String())
@@ -256,7 +268,7 @@
 		Include:      "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", p.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_PATH", p.installDir.String())
 				entries.SetString("LOCAL_SOONG_INSTALLED_MODULE", filepath.Join(p.installDir.String(), fi.stem()))
 				entries.SetString("LOCAL_SOONG_INSTALL_PAIRS",
 					fi.builtFile.String()+":"+filepath.Join(p.installDir.String(), fi.stem()))
@@ -472,10 +484,6 @@
 	inputApex android.Path
 }
 
-func (p *Prebuilt) InstallBypassMake() bool {
-	return true
-}
-
 type ApexFileProperties struct {
 	// the path to the prebuilt .apex file to import.
 	//
@@ -965,7 +973,7 @@
 
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	if a.installable() {
-		ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
+		a.installedFile = ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
 	}
 
 	// in case that apex_set replaces source apex (using prefer: prop)
diff --git a/apex/vndk.go b/apex/vndk.go
index cf525a8..ef3e5e1 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -140,21 +140,5 @@
 		addSymlink("/apex/com.android.i18n/etc/icu", dir, "icu")
 	}
 
-	// TODO(b/124106384): Clean up compat symlinks for ART binaries.
-	if name == "com.android.art" {
-		dir := android.PathForModuleInPartitionInstall(ctx, "system", "bin")
-		addSymlink("/apex/com.android.art/bin/dalvikvm", dir, "dalvikvm")
-		dex2oat := "dex2oat32"
-		if ctx.Config().Android64() {
-			dex2oat = "dex2oat64"
-		}
-		addSymlink("/apex/com.android.art/bin/"+dex2oat, dir, "dex2oat")
-	} else if name == "com.android.art" || strings.HasPrefix(name, "com.android.art.") {
-		dir := android.PathForModuleInPartitionInstall(ctx, "system", "bin")
-		symlinks = append(symlinks,
-			dir.Join(ctx, "dalvikvm"),
-			dir.Join(ctx, "dex2oat"))
-	}
-
 	return symlinks
 }
diff --git a/bazel/configurability.go b/bazel/configurability.go
index 1993f76..7355ac7 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -109,6 +109,21 @@
 		osArchWindowsX86_64:        "//build/bazel/platforms/os_arch:windows_x86_64",
 		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
 	}
+
+	// Map where keys are OsType names, and values are slices containing the archs
+	// that that OS supports.
+	// These definitions copied from arch.go.
+	// TODO(cparsons): Source from arch.go; this task is nontrivial, as it currently results
+	// in a cyclic dependency.
+	osToArchMap = map[string][]string{
+		osAndroid:     {archArm, archArm64, archX86, archX86_64},
+		osLinux:       {archX86, archX86_64},
+		osLinuxMusl:   {archX86, archX86_64},
+		osDarwin:      {archArm64, archX86_64},
+		osLinuxBionic: {archArm64, archX86_64},
+		// TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well.
+		osWindows: {archX86, archX86_64},
+	}
 )
 
 // basic configuration types
@@ -122,6 +137,10 @@
 	productVariables
 )
 
+func osArchString(os string, arch string) string {
+	return fmt.Sprintf("%s_%s", os, arch)
+}
+
 func (ct configurationType) String() string {
 	return map[configurationType]string{
 		noConfig:         "no_config",
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 0bd71c6..5d00b0b 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -14,9 +14,11 @@
 type CcInfo struct {
 	OutputFiles          []string
 	CcObjectFiles        []string
+	CcSharedLibraryFiles []string
 	CcStaticLibraryFiles []string
 	Includes             []string
 	SystemIncludes       []string
+	Headers              []string
 	// Archives owned by the current target (not by its dependencies). These will
 	// be a subset of OutputFiles. (or static libraries, this will be equal to OutputFiles,
 	// but general cc_library will also have dynamic libraries in output files).
@@ -105,13 +107,14 @@
 
 includes = cc_info.compilation_context.includes.to_list()
 system_includes = cc_info.compilation_context.system_includes.to_list()
+headers = [f.path for f in cc_info.compilation_context.headers.to_list()]
 
 ccObjectFiles = []
 staticLibraries = []
 rootStaticArchives = []
 linker_inputs = cc_info.linking_context.linker_inputs.to_list()
 
-static_info_tag = "//build/bazel/rules:cc_library_static.bzl%CcStaticLibraryInfo"
+static_info_tag = "//build/bazel/rules/cc:cc_library_static.bzl%CcStaticLibraryInfo"
 if static_info_tag in providers(target):
   static_info = providers(target)[static_info_tag]
   ccObjectFiles = [f.path for f in static_info.objects]
@@ -126,27 +129,43 @@
         if linker_input.owner == target.label:
           rootStaticArchives.append(library.static_library.path)
 
-rootDynamicLibraries = []
+sharedLibraries = []
+rootSharedLibraries = []
 
 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]
+    path = lib.dynamic_library.path
+    rootSharedLibraries += [path]
+    sharedLibraries.append(path)
+else:
+  for linker_input in linker_inputs:
+    for library in linker_input.libraries:
+      if library.dynamic_library:
+        path = library.dynamic_library.path
+        sharedLibraries.append(path)
+        if linker_input.owner == target.label:
+          rootSharedLibraries.append(path)
 
 toc_file = ""
-toc_file_tag = "//build/bazel/rules:generate_toc.bzl%CcTocInfo"
+toc_file_tag = "//build/bazel/rules/cc:generate_toc.bzl%CcTocInfo"
 if toc_file_tag in providers(target):
   toc_file = providers(target)[toc_file_tag].toc.path
+else:
+  # NOTE: It's OK if there's no ToC, as Soong just uses it for optimization
+  pass
 
 returns = [
   outputFiles,
-  staticLibraries,
   ccObjectFiles,
+  sharedLibraries,
+  staticLibraries,
   includes,
   system_includes,
+  headers,
   rootStaticArchives,
-  rootDynamicLibraries,
+  rootSharedLibraries,
   [toc_file]
 ]
 
@@ -157,30 +176,39 @@
 // The given rawString must correspond to the string output which was created by evaluating the
 // Starlark given in StarlarkFunctionBody.
 func (g getCcInfoType) ParseResult(rawString string) (CcInfo, error) {
-	var outputFiles []string
-	var ccObjects []string
-
+	const expectedLen = 10
 	splitString := strings.Split(rawString, "|")
-	if expectedLen := 8; len(splitString) != expectedLen {
+	if len(splitString) != expectedLen {
 		return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
 	}
 	outputFilesString := splitString[0]
-	ccStaticLibrariesString := splitString[1]
-	ccObjectsString := splitString[2]
-	outputFiles = splitOrEmpty(outputFilesString, ", ")
+	ccObjectsString := splitString[1]
+	ccSharedLibrariesString := splitString[2]
+	ccStaticLibrariesString := splitString[3]
+	includesString := splitString[4]
+	systemIncludesString := splitString[5]
+	headersString := splitString[6]
+	rootStaticArchivesString := splitString[7]
+	rootDynamicLibrariesString := splitString[8]
+	tocFile := splitString[9] // NOTE: Will be the empty string if there wasn't
+
+	outputFiles := splitOrEmpty(outputFilesString, ", ")
+	ccObjects := splitOrEmpty(ccObjectsString, ", ")
+	ccSharedLibraries := splitOrEmpty(ccSharedLibrariesString, ", ")
 	ccStaticLibraries := splitOrEmpty(ccStaticLibrariesString, ", ")
-	ccObjects = splitOrEmpty(ccObjectsString, ", ")
-	includes := splitOrEmpty(splitString[3], ", ")
-	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
+	includes := splitOrEmpty(includesString, ", ")
+	systemIncludes := splitOrEmpty(systemIncludesString, ", ")
+	headers := splitOrEmpty(headersString, ", ")
+	rootStaticArchives := splitOrEmpty(rootStaticArchivesString, ", ")
+	rootDynamicLibraries := splitOrEmpty(rootDynamicLibrariesString, ", ")
 	return CcInfo{
 		OutputFiles:          outputFiles,
 		CcObjectFiles:        ccObjects,
+		CcSharedLibraryFiles: ccSharedLibraries,
 		CcStaticLibraryFiles: ccStaticLibraries,
 		Includes:             includes,
 		SystemIncludes:       systemIncludes,
+		Headers:              headers,
 		RootStaticArchives:   rootStaticArchives,
 		RootDynamicLibraries: rootDynamicLibraries,
 		TocFile:              tocFile,
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 34d0832..606e285 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -63,6 +63,8 @@
 }
 
 func TestGetCcInfoParseResults(t *testing.T) {
+	const expectedSplits = 10
+	noResult := strings.Repeat("|", expectedSplits-1)
 	testCases := []struct {
 		description          string
 		input                string
@@ -71,13 +73,15 @@
 	}{
 		{
 			description: "no result",
-			input:       "|||||||",
+			input:       noResult,
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{},
 				CcObjectFiles:        []string{},
+				CcSharedLibraryFiles: []string{},
 				CcStaticLibraryFiles: []string{},
 				Includes:             []string{},
 				SystemIncludes:       []string{},
+				Headers:              []string{},
 				RootStaticArchives:   []string{},
 				RootDynamicLibraries: []string{},
 				TocFile:              "",
@@ -85,27 +89,56 @@
 		},
 		{
 			description: "only output",
-			input:       "test|||||||",
+			input:       "test" + noResult,
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{"test"},
 				CcObjectFiles:        []string{},
+				CcSharedLibraryFiles: []string{},
 				CcStaticLibraryFiles: []string{},
 				Includes:             []string{},
 				SystemIncludes:       []string{},
+				Headers:              []string{},
 				RootStaticArchives:   []string{},
 				RootDynamicLibraries: []string{},
 				TocFile:              "",
 			},
 		},
 		{
+			description: "only ToC",
+			input:       noResult + "test",
+			expectedOutput: CcInfo{
+				OutputFiles:          []string{},
+				CcObjectFiles:        []string{},
+				CcSharedLibraryFiles: []string{},
+				CcStaticLibraryFiles: []string{},
+				Includes:             []string{},
+				SystemIncludes:       []string{},
+				Headers:              []string{},
+				RootStaticArchives:   []string{},
+				RootDynamicLibraries: []string{},
+				TocFile:              "test",
+			},
+		},
+		{
 			description: "all items set",
-			input:       "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1|lib.so.toc",
+			input: "out1, out2" +
+				"|object1, object2" +
+				"|shared_lib1, shared_lib2" +
+				"|static_lib1, static_lib2" +
+				"|., dir/subdir" +
+				"|system/dir, system/other/dir" +
+				"|dir/subdir/hdr.h" +
+				"|rootstaticarchive1" +
+				"|rootdynamiclibrary1" +
+				"|lib.so.toc",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{"out1", "out2"},
 				CcObjectFiles:        []string{"object1", "object2"},
+				CcSharedLibraryFiles: []string{"shared_lib1", "shared_lib2"},
 				CcStaticLibraryFiles: []string{"static_lib1", "static_lib2"},
 				Includes:             []string{".", "dir/subdir"},
 				SystemIncludes:       []string{"system/dir", "system/other/dir"},
+				Headers:              []string{"dir/subdir/hdr.h"},
 				RootStaticArchives:   []string{"rootstaticarchive1"},
 				RootDynamicLibraries: []string{"rootdynamiclibrary1"},
 				TocFile:              "lib.so.toc",
@@ -115,22 +148,22 @@
 			description:          "too few result splits",
 			input:                "|",
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 8, []string{"", ""}),
+			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, []string{"", ""}),
 		},
 		{
 			description:          "too many result splits",
-			input:                strings.Repeat("|", 8),
+			input:                strings.Repeat("|", expectedSplits+1), // 2 too many
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 8, make([]string, 9)),
+			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", expectedSplits, make([]string, expectedSplits+2)),
 		},
 	}
 	for _, tc := range testCases {
 		actualOutput, err := GetCcInfo.ParseResult(tc.input)
 		if (err == nil && tc.expectedErrorMessage != "") ||
 			(err != nil && err.Error() != tc.expectedErrorMessage) {
-			t.Errorf("%q: expected Error %s, got %s", tc.description, tc.expectedErrorMessage, err)
+			t.Errorf("%q:\nexpected Error %s\n, got %s", tc.description, tc.expectedErrorMessage, err)
 		} else if err == nil && !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
-			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+			t.Errorf("%q:\n expected %#v\n!= actual %#v", tc.description, tc.expectedOutput, actualOutput)
 		}
 	}
 }
diff --git a/bazel/properties.go b/bazel/properties.go
index b370bbf..1300a53 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -107,6 +107,14 @@
 	return dirs
 }
 
+// Add inserts the label Label at the end of the LabelList.
+func (ll *LabelList) Add(label *Label) {
+	if label == nil {
+		return
+	}
+	ll.Includes = append(ll.Includes, *label)
+}
+
 // Append appends the fields of other labelList to the corresponding fields of ll.
 func (ll *LabelList) Append(other LabelList) {
 	if len(ll.Includes) > 0 || len(other.Includes) > 0 {
@@ -236,9 +244,69 @@
 	ConfigurableValues configurableLabels
 }
 
+func (la *LabelAttribute) axisTypes() map[configurationType]bool {
+	types := map[configurationType]bool{}
+	for k := range la.ConfigurableValues {
+		if len(la.ConfigurableValues[k]) > 0 {
+			types[k.configurationType] = true
+		}
+	}
+	return types
+}
+
+// Collapse reduces the configurable axes of the label attribute to a single axis.
+// This is necessary for final writing to bp2build, as a configurable label
+// attribute can only be comprised by a single select.
+func (la *LabelAttribute) Collapse() error {
+	axisTypes := la.axisTypes()
+	_, containsOs := axisTypes[os]
+	_, containsArch := axisTypes[arch]
+	_, containsOsArch := axisTypes[osArch]
+	_, containsProductVariables := axisTypes[productVariables]
+	if containsProductVariables {
+		if containsOs || containsArch || containsOsArch {
+			return fmt.Errorf("label attribute could not be collapsed as it has two or more unrelated axes")
+		}
+	}
+	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
+		// If a bool attribute has both os and arch configuration axes, the only
+		// way to successfully union their values is to increase the granularity
+		// of the configuration criteria to os_arch.
+		for osType, supportedArchs := range osToArchMap {
+			for _, supportedArch := range supportedArchs {
+				osArch := osArchString(osType, supportedArch)
+				if archOsVal := la.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
+					// Do nothing, as the arch_os is explicitly defined already.
+				} else {
+					archVal := la.SelectValue(ArchConfigurationAxis, supportedArch)
+					osVal := la.SelectValue(OsConfigurationAxis, osType)
+					if osVal != nil && archVal != nil {
+						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
+						// runs after os mutator.
+						la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
+					} else if osVal != nil && archVal == nil {
+						la.SetSelectValue(OsArchConfigurationAxis, osArch, *osVal)
+					} else if osVal == nil && archVal != nil {
+						la.SetSelectValue(OsArchConfigurationAxis, osArch, *archVal)
+					}
+				}
+			}
+		}
+		// All os_arch values are now set. Clear os and arch axes.
+		delete(la.ConfigurableValues, ArchConfigurationAxis)
+		delete(la.ConfigurableValues, OsConfigurationAxis)
+	}
+	return nil
+}
+
 // HasConfigurableValues returns whether there are configurable values set for this label.
 func (la LabelAttribute) HasConfigurableValues() bool {
-	return len(la.ConfigurableValues) > 0
+	for _, selectValues := range la.ConfigurableValues {
+		if len(selectValues) > 0 {
+			return true
+		}
+	}
+	return false
 }
 
 // SetValue sets the base, non-configured value for the Label
@@ -263,13 +331,13 @@
 }
 
 // SelectValue gets a value for a bazel select for the given axis and config.
-func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
+func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) *Label {
 	axis.validateConfig(config)
 	switch axis.configurationType {
 	case noConfig:
-		return *la.Value
+		return la.Value
 	case arch, os, osArch, productVariables:
-		return *la.ConfigurableValues[axis][config]
+		return la.ConfigurableValues[axis][config]
 	default:
 		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
 	}
@@ -316,7 +384,12 @@
 
 // HasConfigurableValues returns whether there are configurable values for this attribute.
 func (ba BoolAttribute) HasConfigurableValues() bool {
-	return len(ba.ConfigurableValues) > 0
+	for _, cfgToBools := range ba.ConfigurableValues {
+		if len(cfgToBools) > 0 {
+			return true
+		}
+	}
+	return false
 }
 
 // SetSelectValue sets value for the given axis/config.
@@ -335,6 +408,106 @@
 	}
 }
 
+// ToLabelListAttribute creates and returns a LabelListAttribute from this
+// bool attribute, where each bool in this attribute corresponds to a
+// label list value in the resultant attribute.
+func (ba *BoolAttribute) ToLabelListAttribute(falseVal LabelList, trueVal LabelList) (LabelListAttribute, error) {
+	getLabelList := func(boolPtr *bool) LabelList {
+		if boolPtr == nil {
+			return LabelList{nil, nil}
+		} else if *boolPtr {
+			return trueVal
+		} else {
+			return falseVal
+		}
+	}
+
+	mainVal := getLabelList(ba.Value)
+	if !ba.HasConfigurableValues() {
+		return MakeLabelListAttribute(mainVal), nil
+	}
+
+	result := LabelListAttribute{}
+	if err := ba.Collapse(); err != nil {
+		return result, err
+	}
+
+	for axis, configToBools := range ba.ConfigurableValues {
+		if len(configToBools) < 1 {
+			continue
+		}
+		for config, boolPtr := range configToBools {
+			val := getLabelList(&boolPtr)
+			if !val.Equals(mainVal) {
+				result.SetSelectValue(axis, config, val)
+			}
+		}
+		result.SetSelectValue(axis, ConditionsDefaultConfigKey, mainVal)
+	}
+
+	return result, nil
+}
+
+// Collapse reduces the configurable axes of the boolean attribute to a single axis.
+// This is necessary for final writing to bp2build, as a configurable boolean
+// attribute can only be comprised by a single select.
+func (ba *BoolAttribute) Collapse() error {
+	axisTypes := ba.axisTypes()
+	_, containsOs := axisTypes[os]
+	_, containsArch := axisTypes[arch]
+	_, containsOsArch := axisTypes[osArch]
+	_, containsProductVariables := axisTypes[productVariables]
+	if containsProductVariables {
+		if containsOs || containsArch || containsOsArch {
+			return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
+		}
+	}
+	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
+		// If a bool attribute has both os and arch configuration axes, the only
+		// way to successfully union their values is to increase the granularity
+		// of the configuration criteria to os_arch.
+		for osType, supportedArchs := range osToArchMap {
+			for _, supportedArch := range supportedArchs {
+				osArch := osArchString(osType, supportedArch)
+				if archOsVal := ba.SelectValue(OsArchConfigurationAxis, osArch); archOsVal != nil {
+					// Do nothing, as the arch_os is explicitly defined already.
+				} else {
+					archVal := ba.SelectValue(ArchConfigurationAxis, supportedArch)
+					osVal := ba.SelectValue(OsConfigurationAxis, osType)
+					if osVal != nil && archVal != nil {
+						// In this case, arch takes precedence. (This fits legacy Soong behavior, as arch mutator
+						// runs after os mutator.
+						ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
+					} else if osVal != nil && archVal == nil {
+						ba.SetSelectValue(OsArchConfigurationAxis, osArch, osVal)
+					} else if osVal == nil && archVal != nil {
+						ba.SetSelectValue(OsArchConfigurationAxis, osArch, archVal)
+					}
+				}
+			}
+		}
+		// All os_arch values are now set. Clear os and arch axes.
+		delete(ba.ConfigurableValues, ArchConfigurationAxis)
+		delete(ba.ConfigurableValues, OsConfigurationAxis)
+		// Verify post-condition; this should never fail, provided no additional
+		// axes are introduced.
+		if len(ba.ConfigurableValues) > 1 {
+			panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
+		}
+	}
+	return nil
+}
+
+func (ba *BoolAttribute) axisTypes() map[configurationType]bool {
+	types := map[configurationType]bool{}
+	for k := range ba.ConfigurableValues {
+		if len(ba.ConfigurableValues[k]) > 0 {
+			types[k.configurationType] = true
+		}
+	}
+	return types
+}
+
 // SelectValue gets the value for the given axis/config.
 func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
 	axis.validateConfig(config)
@@ -366,9 +539,23 @@
 // labelListSelectValues supports config-specific label_list typed Bazel attribute values.
 type labelListSelectValues map[string]LabelList
 
-func (ll labelListSelectValues) appendSelects(other labelListSelectValues) {
+func (ll labelListSelectValues) addSelects(label labelSelectValues) {
+	for k, v := range label {
+		if label == nil {
+			continue
+		}
+		l := ll[k]
+		(&l).Add(v)
+		ll[k] = l
+	}
+}
+
+func (ll labelListSelectValues) appendSelects(other labelListSelectValues, forceSpecifyEmptyList bool) {
 	for k, v := range other {
 		l := ll[k]
+		if forceSpecifyEmptyList && l.IsNil() && !v.IsNil() {
+			l.Includes = []Label{}
+		}
 		(&l).Append(v)
 		ll[k] = l
 	}
@@ -424,17 +611,22 @@
 	cll[axis][config] = list
 }
 
-func (cll configurableLabelLists) Append(other configurableLabelLists) {
+func (cll configurableLabelLists) Append(other configurableLabelLists, forceSpecifyEmptyList bool) {
 	for axis, otherSelects := range other {
 		selects := cll[axis]
 		if selects == nil {
 			selects = make(labelListSelectValues, len(otherSelects))
 		}
-		selects.appendSelects(otherSelects)
+		selects.appendSelects(otherSelects, forceSpecifyEmptyList)
 		cll[axis] = selects
 	}
 }
 
+func (lla *LabelListAttribute) Clone() *LabelListAttribute {
+	result := &LabelListAttribute{ForceSpecifyEmptyList: lla.ForceSpecifyEmptyList}
+	return result.Append(*lla)
+}
+
 // MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
 func MakeLabelListAttribute(value LabelList) LabelListAttribute {
 	return LabelListAttribute{
@@ -488,21 +680,47 @@
 }
 
 // Append all values, including os and arch specific ones, from another
-// LabelListAttribute to this LabelListAttribute.
-func (lla *LabelListAttribute) Append(other LabelListAttribute) {
-	if lla.ForceSpecifyEmptyList && !other.Value.IsNil() {
+// LabelListAttribute to this LabelListAttribute. Returns this LabelListAttribute.
+func (lla *LabelListAttribute) Append(other LabelListAttribute) *LabelListAttribute {
+	forceSpecifyEmptyList := lla.ForceSpecifyEmptyList || other.ForceSpecifyEmptyList
+	if forceSpecifyEmptyList && lla.Value.IsNil() && !other.Value.IsNil() {
 		lla.Value.Includes = []Label{}
 	}
 	lla.Value.Append(other.Value)
 	if lla.ConfigurableValues == nil {
 		lla.ConfigurableValues = make(configurableLabelLists)
 	}
-	lla.ConfigurableValues.Append(other.ConfigurableValues)
+	lla.ConfigurableValues.Append(other.ConfigurableValues, forceSpecifyEmptyList)
+	return lla
+}
+
+// Add inserts the labels for each axis of LabelAttribute at the end of corresponding axis's
+// LabelList within the LabelListAttribute
+func (lla *LabelListAttribute) Add(label *LabelAttribute) {
+	if label == nil {
+		return
+	}
+
+	lla.Value.Add(label.Value)
+	if lla.ConfigurableValues == nil && label.ConfigurableValues != nil {
+		lla.ConfigurableValues = make(configurableLabelLists)
+	}
+	for axis, _ := range label.ConfigurableValues {
+		if _, exists := lla.ConfigurableValues[axis]; !exists {
+			lla.ConfigurableValues[axis] = make(labelListSelectValues)
+		}
+		lla.ConfigurableValues[axis].addSelects(label.ConfigurableValues[axis])
+	}
 }
 
 // HasConfigurableValues returns true if the attribute contains axis-specific label list values.
 func (lla LabelListAttribute) HasConfigurableValues() bool {
-	return len(lla.ConfigurableValues) > 0
+	for _, selectValues := range lla.ConfigurableValues {
+		if len(selectValues) > 0 {
+			return true
+		}
+	}
+	return false
 }
 
 // IsEmpty returns true if the attribute has no values under any configuration.
@@ -518,6 +736,24 @@
 	return true
 }
 
+// IsNil returns true if the attribute has not been set for any configuration.
+func (lla LabelListAttribute) IsNil() bool {
+	if lla.Value.Includes != nil {
+		return false
+	}
+	return !lla.HasConfigurableValues()
+}
+
+// Exclude for the given axis, config, removes Includes in labelList from Includes and appends them
+// to Excludes. This is to special case any excludes that are not specified in a bp file but need to
+// be removed, e.g. if they could cause duplicate element failures.
+func (lla *LabelListAttribute) Exclude(axis ConfigurationAxis, config string, labelList LabelList) {
+	val := lla.SelectValue(axis, config)
+	newList := SubtractBazelLabelList(val, labelList)
+	newList.Excludes = append(newList.Excludes, labelList.Includes...)
+	lla.SetSelectValue(axis, config, newList)
+}
+
 // ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
 // the base value and included in default values as appropriate.
 func (lla *LabelListAttribute) ResolveExcludes() {
@@ -566,7 +802,7 @@
 
 // LabelMapper is a function that takes a OtherModuleContext and returns a (potentially changed)
 // label and whether it was changed.
-type LabelMapper func(OtherModuleContext, string) (string, bool)
+type LabelMapper func(OtherModuleContext, Label) (string, bool)
 
 // LabelPartition contains descriptions of a partition for labels
 type LabelPartition struct {
@@ -588,7 +824,7 @@
 // not.
 func (lf LabelPartition) filter(ctx OtherModuleContext, label Label) *Label {
 	if lf.LabelMapper != nil {
-		if newLabel, changed := lf.LabelMapper(ctx, label.Label); changed {
+		if newLabel, changed := lf.LabelMapper(ctx, label); changed {
 			return &Label{newLabel, label.OriginalModuleName}
 		}
 	}
@@ -752,17 +988,28 @@
 
 // HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
 func (sla StringListAttribute) HasConfigurableValues() bool {
-	return len(sla.ConfigurableValues) > 0
+	for _, selectValues := range sla.ConfigurableValues {
+		if len(selectValues) > 0 {
+			return true
+		}
+	}
+	return false
 }
 
 // Append appends all values, including os and arch specific ones, from another
 // StringListAttribute to this StringListAttribute
-func (sla *StringListAttribute) Append(other StringListAttribute) {
+func (sla *StringListAttribute) Append(other StringListAttribute) *StringListAttribute {
 	sla.Value = append(sla.Value, other.Value...)
 	if sla.ConfigurableValues == nil {
 		sla.ConfigurableValues = make(configurableStringLists)
 	}
 	sla.ConfigurableValues.Append(other.ConfigurableValues)
+	return sla
+}
+
+func (sla *StringListAttribute) Clone() *StringListAttribute {
+	result := &StringListAttribute{}
+	return result.Append(*sla)
 }
 
 // SetSelectValue set a value for a bazel select for the given axis, config and value.
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 7a7d6f3..c7f9776 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -313,16 +313,16 @@
 // labelAddSuffixForTypeMapper returns a LabelMapper that adds suffix to label name for modules of
 // typ
 func labelAddSuffixForTypeMapper(suffix, typ string) LabelMapper {
-	return func(omc OtherModuleContext, label string) (string, bool) {
-		m, ok := omc.ModuleFromName(label)
+	return func(omc OtherModuleContext, label Label) (string, bool) {
+		m, ok := omc.ModuleFromName(label.Label)
 		if !ok {
-			return label, false
+			return label.Label, false
 		}
 		mTyp := omc.OtherModuleType(m)
 		if typ == mTyp {
-			return label + suffix, true
+			return label.Label + suffix, true
 		}
-		return label, false
+		return label.Label, false
 	}
 }
 
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 9b66354..b904c35 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -19,6 +19,7 @@
     deps: [
         "soong-android",
         "soong-android-soongconfig",
+        "soong-shared",
         "soong-apex",
         "soong-bazel",
         "soong-cc",
@@ -27,18 +28,21 @@
         "soong-genrule",
         "soong-python",
         "soong-sh",
+        "soong-starlark-format",
+        "soong-ui-metrics",
     ],
     testSrcs: [
         "android_app_certificate_conversion_test.go",
+        "android_app_conversion_test.go",
         "apex_conversion_test.go",
         "apex_key_conversion_test.go",
         "build_conversion_test.go",
         "bzl_conversion_test.go",
-        "cc_genrule_conversion_test.go",
+        "cc_binary_conversion_test.go",
         "cc_library_conversion_test.go",
         "cc_library_headers_conversion_test.go",
-        "cc_library_static_conversion_test.go",
         "cc_library_shared_conversion_test.go",
+        "cc_library_static_conversion_test.go",
         "cc_object_conversion_test.go",
         "conversion_test.go",
         "filegroup_conversion_test.go",
@@ -48,6 +52,7 @@
         "python_binary_conversion_test.go",
         "python_library_conversion_test.go",
         "sh_conversion_test.go",
+        "soong_config_module_type_conversion_test.go",
         "testing.go",
     ],
     pluginFor: [
diff --git a/bp2build/android_app_certificate_conversion_test.go b/bp2build/android_app_certificate_conversion_test.go
index 6a53b00..035a352 100644
--- a/bp2build/android_app_certificate_conversion_test.go
+++ b/bp2build/android_app_certificate_conversion_test.go
@@ -31,11 +31,10 @@
 
 func TestAndroidAppCertificateSimple(t *testing.T) {
 	runAndroidAppCertificateTestCase(t, bp2buildTestCase{
-		description:                        "Android app certificate - simple example",
-		moduleTypeUnderTest:                "android_app_certificate",
-		moduleTypeUnderTestFactory:         java.AndroidAppCertificateFactory,
-		moduleTypeUnderTestBp2BuildMutator: java.AndroidAppCertificateBp2Build,
-		filesystem:                         map[string]string{},
+		description:                "Android app certificate - simple example",
+		moduleTypeUnderTest:        "android_app_certificate",
+		moduleTypeUnderTestFactory: java.AndroidAppCertificateFactory,
+		filesystem:                 map[string]string{},
 		blueprint: `
 android_app_certificate {
         name: "com.android.apogee.cert",
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
new file mode 100644
index 0000000..28de06c
--- /dev/null
+++ b/bp2build/android_app_conversion_test.go
@@ -0,0 +1,92 @@
+// 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/java"
+
+	"testing"
+)
+
+func runAndroidAppTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerAndroidAppModuleTypes, tc)
+}
+
+func registerAndroidAppModuleTypes(ctx android.RegistrationContext) {
+}
+
+func TestMinimalAndroidApp(t *testing.T) {
+	runAndroidAppTestCase(t, bp2buildTestCase{
+		description:                "Android app - simple example",
+		moduleTypeUnderTest:        "android_app",
+		moduleTypeUnderTestFactory: java.AndroidAppFactory,
+		filesystem: map[string]string{
+			"app.java":            "",
+			"res/res.png":         "",
+			"AndroidManifest.xml": "",
+		},
+		blueprint: `
+android_app {
+        name: "TestApp",
+        srcs: ["app.java"],
+        sdk_version: "current",
+}
+`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("android_binary", "TestApp", attrNameToString{
+				"srcs":           `["app.java"]`,
+				"manifest":       `"AndroidManifest.xml"`,
+				"resource_files": `["res/res.png"]`,
+			}),
+		}})
+}
+
+func TestAndroidAppAllSupportedFields(t *testing.T) {
+	runAndroidAppTestCase(t, bp2buildTestCase{
+		description:                "Android app - all supported fields",
+		moduleTypeUnderTest:        "android_app",
+		moduleTypeUnderTestFactory: java.AndroidAppFactory,
+		filesystem: map[string]string{
+			"app.java":                     "",
+			"resa/res.png":                 "",
+			"resb/res.png":                 "",
+			"manifest/AndroidManifest.xml": "",
+		},
+		blueprint: simpleModuleDoNotConvertBp2build("android_app", "static_lib_dep") + `
+android_app {
+        name: "TestApp",
+        srcs: ["app.java"],
+        sdk_version: "current",
+        package_name: "com.google",
+        resource_dirs: ["resa", "resb"],
+        manifest: "manifest/AndroidManifest.xml",
+        static_libs: ["static_lib_dep"]
+}
+`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("android_binary", "TestApp", attrNameToString{
+				"srcs":     `["app.java"]`,
+				"manifest": `"manifest/AndroidManifest.xml"`,
+				"resource_files": `[
+        "resa/res.png",
+        "resb/res.png",
+    ]`,
+				"custom_package": `"com.google"`,
+				"deps":           `[":static_lib_dep"]`,
+			}),
+		}})
+}
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index 1a23db7..9057189 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -19,6 +19,7 @@
 	"android/soong/apex"
 	"android/soong/cc"
 	"android/soong/java"
+	"android/soong/sh"
 
 	"testing"
 )
@@ -32,6 +33,8 @@
 	// CC module types needed as they can be APEX dependencies
 	cc.RegisterCCBuildComponents(ctx)
 
+	ctx.RegisterModuleType("sh_binary", sh.ShBinaryFactory)
+	ctx.RegisterModuleType("cc_binary", cc.BinaryFactory)
 	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
 	ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory)
 	ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
@@ -40,72 +43,75 @@
 
 func TestApexBundleSimple(t *testing.T) {
 	runApexTestCase(t, bp2buildTestCase{
-		description:                        "apex - simple example",
-		moduleTypeUnderTest:                "apex",
-		moduleTypeUnderTestFactory:         apex.BundleFactory,
-		moduleTypeUnderTestBp2BuildMutator: apex.ApexBundleBp2Build,
-		filesystem:                         map[string]string{},
+		description:                "apex - example with all props",
+		moduleTypeUnderTest:        "apex",
+		moduleTypeUnderTestFactory: apex.BundleFactory,
+		filesystem:                 map[string]string{},
 		blueprint: `
 apex_key {
-        name: "com.android.apogee.key",
-        public_key: "com.android.apogee.avbpubkey",
-        private_key: "com.android.apogee.pem",
+	name: "com.android.apogee.key",
+	public_key: "com.android.apogee.avbpubkey",
+	private_key: "com.android.apogee.pem",
 	bazel_module: { bp2build_available: false },
 }
 
 android_app_certificate {
-        name: "com.android.apogee.certificate",
-        certificate: "com.android.apogee",
-        bazel_module: { bp2build_available: false },
-}
-
-cc_library {
-        name: "native_shared_lib_1",
+	name: "com.android.apogee.certificate",
+	certificate: "com.android.apogee",
 	bazel_module: { bp2build_available: false },
 }
 
 cc_library {
-        name: "native_shared_lib_2",
+	name: "native_shared_lib_1",
+	bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+	name: "native_shared_lib_2",
 	bazel_module: { bp2build_available: false },
 }
 
 // TODO(b/194878861): Add bp2build support for prebuilt_etc
 cc_library {
-        name: "pretend_prebuilt_1",
-        bazel_module: { bp2build_available: false },
+	name: "pretend_prebuilt_1",
+	bazel_module: { bp2build_available: false },
 }
 
 // TODO(b/194878861): Add bp2build support for prebuilt_etc
 cc_library {
-        name: "pretend_prebuilt_2",
-        bazel_module: { bp2build_available: false },
+	name: "pretend_prebuilt_2",
+	bazel_module: { bp2build_available: false },
 }
 
 filegroup {
 	name: "com.android.apogee-file_contexts",
-        srcs: [
-                "com.android.apogee-file_contexts",
-        ],
-        bazel_module: { bp2build_available: false },
+	srcs: [
+			"com.android.apogee-file_contexts",
+	],
+	bazel_module: { bp2build_available: false },
 }
 
+cc_binary { name: "cc_binary_1", bazel_module: { bp2build_available: false } }
+sh_binary { name: "sh_binary_2", bazel_module: { bp2build_available: false } }
+
 apex {
 	name: "com.android.apogee",
 	manifest: "apogee_manifest.json",
 	androidManifest: "ApogeeAndroidManifest.xml",
-        file_contexts: "com.android.apogee-file_contexts",
+	file_contexts: "com.android.apogee-file_contexts",
 	min_sdk_version: "29",
 	key: "com.android.apogee.key",
 	certificate: "com.android.apogee.certificate",
 	updatable: false,
 	installable: false,
+	compressible: false,
 	native_shared_libs: [
 	    "native_shared_lib_1",
 	    "native_shared_lib_2",
 	],
 	binaries: [
-            "binary_1",
-	    "binary_2",
+		"cc_binary_1",
+		"sh_binary_2",
 	],
 	prebuilts: [
 	    "pretend_prebuilt_1",
@@ -117,8 +123,8 @@
 			makeBazelTarget("apex", "com.android.apogee", attrNameToString{
 				"android_manifest": `"ApogeeAndroidManifest.xml"`,
 				"binaries": `[
-        "binary_1",
-        "binary_2",
+        ":cc_binary_1",
+        ":sh_binary_2",
     ]`,
 				"certificate":     `":com.android.apogee.certificate"`,
 				"file_contexts":   `":com.android.apogee-file_contexts"`,
@@ -126,26 +132,157 @@
 				"key":             `":com.android.apogee.key"`,
 				"manifest":        `"apogee_manifest.json"`,
 				"min_sdk_version": `"29"`,
-				"native_shared_libs": `[
+				"native_shared_libs_32": `[
         ":native_shared_lib_1",
         ":native_shared_lib_2",
     ]`,
+				"native_shared_libs_64": `select({
+        "//build/bazel/platforms/arch:arm64": [
+            ":native_shared_lib_1",
+            ":native_shared_lib_2",
+        ],
+        "//build/bazel/platforms/arch:x86_64": [
+            ":native_shared_lib_1",
+            ":native_shared_lib_2",
+        ],
+        "//conditions:default": [],
+    })`,
 				"prebuilts": `[
         ":pretend_prebuilt_1",
         ":pretend_prebuilt_2",
     ]`,
-				"updatable": "False",
+				"updatable":    "False",
+				"compressible": "False",
+			}),
+		}})
+}
+
+func TestApexBundleCompileMultilibBoth(t *testing.T) {
+	runApexTestCase(t, bp2buildTestCase{
+		description:                "apex - example with compile_multilib=both",
+		moduleTypeUnderTest:        "apex",
+		moduleTypeUnderTestFactory: apex.BundleFactory,
+		filesystem:                 map[string]string{},
+		blueprint:                  createMultilibBlueprint("both"),
+		expectedBazelTargets: []string{
+			makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+				"native_shared_libs_32": `[
+        ":native_shared_lib_1",
+        ":native_shared_lib_3",
+    ] + select({
+        "//build/bazel/platforms/arch:arm": [":native_shared_lib_2"],
+        "//build/bazel/platforms/arch:x86": [":native_shared_lib_2"],
+        "//conditions:default": [],
+    })`,
+				"native_shared_libs_64": `select({
+        "//build/bazel/platforms/arch:arm64": [
+            ":native_shared_lib_1",
+            ":native_shared_lib_4",
+            ":native_shared_lib_2",
+        ],
+        "//build/bazel/platforms/arch:x86_64": [
+            ":native_shared_lib_1",
+            ":native_shared_lib_4",
+            ":native_shared_lib_2",
+        ],
+        "//conditions:default": [],
+    })`,
+			}),
+		}})
+}
+
+func TestApexBundleCompileMultilibFirst(t *testing.T) {
+	runApexTestCase(t, bp2buildTestCase{
+		description:                "apex - example with compile_multilib=first",
+		moduleTypeUnderTest:        "apex",
+		moduleTypeUnderTestFactory: apex.BundleFactory,
+		filesystem:                 map[string]string{},
+		blueprint:                  createMultilibBlueprint("first"),
+		expectedBazelTargets: []string{
+			makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+				"native_shared_libs_32": `select({
+        "//build/bazel/platforms/arch:arm": [
+            ":native_shared_lib_1",
+            ":native_shared_lib_3",
+            ":native_shared_lib_2",
+        ],
+        "//build/bazel/platforms/arch:x86": [
+            ":native_shared_lib_1",
+            ":native_shared_lib_3",
+            ":native_shared_lib_2",
+        ],
+        "//conditions:default": [],
+    })`,
+				"native_shared_libs_64": `select({
+        "//build/bazel/platforms/arch:arm64": [
+            ":native_shared_lib_1",
+            ":native_shared_lib_4",
+            ":native_shared_lib_2",
+        ],
+        "//build/bazel/platforms/arch:x86_64": [
+            ":native_shared_lib_1",
+            ":native_shared_lib_4",
+            ":native_shared_lib_2",
+        ],
+        "//conditions:default": [],
+    })`,
+			}),
+		}})
+}
+
+func TestApexBundleCompileMultilib32(t *testing.T) {
+	runApexTestCase(t, bp2buildTestCase{
+		description:                "apex - example with compile_multilib=32",
+		moduleTypeUnderTest:        "apex",
+		moduleTypeUnderTestFactory: apex.BundleFactory,
+		filesystem:                 map[string]string{},
+		blueprint:                  createMultilibBlueprint("32"),
+		expectedBazelTargets: []string{
+			makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+				"native_shared_libs_32": `[
+        ":native_shared_lib_1",
+        ":native_shared_lib_3",
+    ] + select({
+        "//build/bazel/platforms/arch:arm": [":native_shared_lib_2"],
+        "//build/bazel/platforms/arch:x86": [":native_shared_lib_2"],
+        "//conditions:default": [],
+    })`,
+			}),
+		}})
+}
+
+func TestApexBundleCompileMultilib64(t *testing.T) {
+	runApexTestCase(t, bp2buildTestCase{
+		description:                "apex - example with compile_multilib=64",
+		moduleTypeUnderTest:        "apex",
+		moduleTypeUnderTestFactory: apex.BundleFactory,
+		filesystem:                 map[string]string{},
+		blueprint:                  createMultilibBlueprint("64"),
+		expectedBazelTargets: []string{
+			makeBazelTarget("apex", "com.android.apogee", attrNameToString{
+				"native_shared_libs_64": `select({
+        "//build/bazel/platforms/arch:arm64": [
+            ":native_shared_lib_1",
+            ":native_shared_lib_4",
+            ":native_shared_lib_2",
+        ],
+        "//build/bazel/platforms/arch:x86_64": [
+            ":native_shared_lib_1",
+            ":native_shared_lib_4",
+            ":native_shared_lib_2",
+        ],
+        "//conditions:default": [],
+    })`,
 			}),
 		}})
 }
 
 func TestApexBundleDefaultPropertyValues(t *testing.T) {
 	runApexTestCase(t, bp2buildTestCase{
-		description:                        "apex - default property values",
-		moduleTypeUnderTest:                "apex",
-		moduleTypeUnderTestFactory:         apex.BundleFactory,
-		moduleTypeUnderTestBp2BuildMutator: apex.ApexBundleBp2Build,
-		filesystem:                         map[string]string{},
+		description:                "apex - default property values",
+		moduleTypeUnderTest:        "apex",
+		moduleTypeUnderTestFactory: apex.BundleFactory,
+		filesystem:                 map[string]string{},
 		blueprint: `
 apex {
 	name: "com.android.apogee",
@@ -160,11 +297,10 @@
 
 func TestApexBundleHasBazelModuleProps(t *testing.T) {
 	runApexTestCase(t, bp2buildTestCase{
-		description:                        "apex - has bazel module props",
-		moduleTypeUnderTest:                "apex",
-		moduleTypeUnderTestFactory:         apex.BundleFactory,
-		moduleTypeUnderTestBp2BuildMutator: apex.ApexBundleBp2Build,
-		filesystem:                         map[string]string{},
+		description:                "apex - has bazel module props",
+		moduleTypeUnderTest:        "apex",
+		moduleTypeUnderTestFactory: apex.BundleFactory,
+		filesystem:                 map[string]string{},
 		blueprint: `
 apex {
 	name: "apogee",
@@ -177,3 +313,53 @@
 		}),
 		}})
 }
+
+func createMultilibBlueprint(compile_multilib string) string {
+	return `
+cc_library {
+	name: "native_shared_lib_1",
+	bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+	name: "native_shared_lib_2",
+	bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+	name: "native_shared_lib_3",
+	bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+	name: "native_shared_lib_4",
+	bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	compile_multilib: "` + compile_multilib + `",
+	multilib: {
+		both: {
+			native_shared_libs: [
+				"native_shared_lib_1",
+			],
+		},
+		first: {
+			native_shared_libs: [
+				"native_shared_lib_2",
+			],
+		},
+		lib32: {
+			native_shared_libs: [
+				"native_shared_lib_3",
+			],
+		},
+		lib64: {
+			native_shared_libs: [
+				"native_shared_lib_4",
+			],
+		},
+	},
+}`
+}
diff --git a/bp2build/apex_key_conversion_test.go b/bp2build/apex_key_conversion_test.go
index 17f79a6..1d949901 100644
--- a/bp2build/apex_key_conversion_test.go
+++ b/bp2build/apex_key_conversion_test.go
@@ -31,11 +31,10 @@
 
 func TestApexKeySimple(t *testing.T) {
 	runApexKeyTestCase(t, bp2buildTestCase{
-		description:                        "apex key - simple example",
-		moduleTypeUnderTest:                "apex_key",
-		moduleTypeUnderTestFactory:         apex.ApexKeyFactory,
-		moduleTypeUnderTestBp2BuildMutator: apex.ApexKeyBp2Build,
-		filesystem:                         map[string]string{},
+		description:                "apex key - simple example",
+		moduleTypeUnderTest:        "apex_key",
+		moduleTypeUnderTestFactory: apex.ApexKeyFactory,
+		filesystem:                 map[string]string{},
 		blueprint: `
 apex_key {
         name: "com.android.apogee.key",
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index eb60cd1..1d3b105 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -27,6 +27,7 @@
 
 	"android/soong/android"
 	"android/soong/bazel"
+	"android/soong/starlark_fmt"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -259,7 +260,9 @@
 
 	// Simple metrics tracking for bp2build
 	metrics := CodegenMetrics{
-		ruleClassCount: make(map[string]int),
+		ruleClassCount:           make(map[string]uint64),
+		convertedModuleTypeCount: make(map[string]uint64),
+		totalModuleTypeCount:     make(map[string]uint64),
 	}
 
 	dirs := make(map[string]bool)
@@ -269,6 +272,7 @@
 	bpCtx := ctx.Context()
 	bpCtx.VisitAllModules(func(m blueprint.Module) {
 		dir := bpCtx.ModuleDir(m)
+		moduleType := bpCtx.ModuleType(m)
 		dirs[dir] = true
 
 		var targets []BazelTarget
@@ -292,7 +296,7 @@
 				// targets in the same BUILD file (or package).
 
 				// Log the module.
-				metrics.AddConvertedModule(m.Name(), Handcrafted)
+				metrics.AddConvertedModule(m, moduleType, Handcrafted)
 
 				pathToBuildFile := getBazelPackagePath(b)
 				if _, exists := buildFileToAppend[pathToBuildFile]; exists {
@@ -312,7 +316,7 @@
 				// Handle modules converted to generated targets.
 
 				// Log the module.
-				metrics.AddConvertedModule(m.Name(), Generated)
+				metrics.AddConvertedModule(aModule, moduleType, Generated)
 
 				// Handle modules with unconverted deps. By default, emit a warning.
 				if unconvertedDeps := aModule.GetUnconvertedBp2buildDeps(); len(unconvertedDeps) > 0 {
@@ -324,6 +328,15 @@
 						return
 					}
 				}
+				if unconvertedDeps := aModule.GetMissingBp2buildDeps(); len(unconvertedDeps) > 0 {
+					msg := fmt.Sprintf("%q depends on missing modules: %s", m.Name(), strings.Join(unconvertedDeps, ", "))
+					if ctx.unconvertedDepMode == warnUnconvertedDeps {
+						metrics.moduleWithMissingDepsMsgs = append(metrics.moduleWithMissingDepsMsgs, msg)
+					} else if ctx.unconvertedDepMode == errorModulesUnconvertedDeps {
+						errs = append(errs, fmt.Errorf(msg))
+						return
+					}
+				}
 				targets = generateBazelTargets(bpCtx, aModule)
 				for _, t := range targets {
 					// A module can potentially generate more than 1 Bazel
@@ -331,7 +344,7 @@
 					metrics.IncrementRuleClassCount(t.ruleClass)
 				}
 			} else {
-				metrics.IncrementUnconvertedCount()
+				metrics.AddUnconvertedModule(moduleType)
 				return
 			}
 		case QueryView:
@@ -547,48 +560,27 @@
 		return "", nil
 	}
 
-	var ret string
 	switch propertyValue.Kind() {
 	case reflect.String:
-		ret = fmt.Sprintf("\"%v\"", escapeString(propertyValue.String()))
+		return fmt.Sprintf("\"%v\"", escapeString(propertyValue.String())), nil
 	case reflect.Bool:
-		ret = strings.Title(fmt.Sprintf("%v", propertyValue.Interface()))
+		return starlark_fmt.PrintBool(propertyValue.Bool()), nil
 	case reflect.Int, reflect.Uint, reflect.Int64:
-		ret = fmt.Sprintf("%v", propertyValue.Interface())
+		return fmt.Sprintf("%v", propertyValue.Interface()), nil
 	case reflect.Ptr:
 		return prettyPrint(propertyValue.Elem(), indent, emitZeroValues)
 	case reflect.Slice:
-		if propertyValue.Len() == 0 {
-			return "[]", nil
-		}
-
-		if propertyValue.Len() == 1 {
-			// Single-line list for list with only 1 element
-			ret += "["
-			indexedValue, err := prettyPrint(propertyValue.Index(0), indent, emitZeroValues)
+		elements := make([]string, 0, propertyValue.Len())
+		for i := 0; i < propertyValue.Len(); i++ {
+			val, err := prettyPrint(propertyValue.Index(i), indent, emitZeroValues)
 			if err != nil {
 				return "", err
 			}
-			ret += indexedValue
-			ret += "]"
-		} else {
-			// otherwise, use a multiline list.
-			ret += "[\n"
-			for i := 0; i < propertyValue.Len(); i++ {
-				indexedValue, err := prettyPrint(propertyValue.Index(i), indent+1, emitZeroValues)
-				if err != nil {
-					return "", err
-				}
-
-				if indexedValue != "" {
-					ret += makeIndent(indent + 1)
-					ret += indexedValue
-					ret += ",\n"
-				}
+			if val != "" {
+				elements = append(elements, val)
 			}
-			ret += makeIndent(indent)
-			ret += "]"
 		}
+		return starlark_fmt.PrintList(elements, indent, "%s"), nil
 
 	case reflect.Struct:
 		// Special cases where the bp2build sends additional information to the codegenerator
@@ -599,18 +591,12 @@
 			return fmt.Sprintf("%q", label.Label), nil
 		}
 
-		ret = "{\n"
 		// Sort and print the struct props by the key.
 		structProps := extractStructProperties(propertyValue, indent)
 		if len(structProps) == 0 {
 			return "", nil
 		}
-		for _, k := range android.SortedStringKeys(structProps) {
-			ret += makeIndent(indent + 1)
-			ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
-		}
-		ret += makeIndent(indent)
-		ret += "}"
+		return starlark_fmt.PrintDict(structProps, indent), nil
 	case reflect.Interface:
 		// TODO(b/164227191): implement pretty print for interfaces.
 		// Interfaces are used for for arch, multilib and target properties.
@@ -619,7 +605,6 @@
 		return "", fmt.Errorf(
 			"unexpected kind for property struct field: %s", propertyValue.Kind())
 	}
-	return ret, nil
 }
 
 // Converts a reflected property struct value into a map of property names and property values,
@@ -724,13 +709,6 @@
 	return strings.ReplaceAll(s, "\"", "\\\"")
 }
 
-func makeIndent(indent int) string {
-	if indent < 0 {
-		panic(fmt.Errorf("indent column cannot be less than 0, but got %d", indent))
-	}
-	return strings.Repeat("    ", indent)
-}
-
 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 95a26a9..b21a477 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -245,7 +245,7 @@
 		{
 			description: "string props",
 			blueprint: `custom {
-	name: "foo",
+  name: "foo",
     string_list_prop: ["a", "b"],
     string_ptr_prop: "a",
     bazel_module: { bp2build_available: true },
@@ -301,6 +301,19 @@
 			},
 		},
 		{
+			description: "non-existent dep",
+			blueprint: `custom {
+  name: "has_dep",
+  arch_paths: [":dep"],
+  bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTarget("custom", "has_dep", attrNameToString{
+					"arch_paths": `[":dep__BP2BUILD__MISSING__DEP"]`,
+				}),
+			},
+		},
+		{
 			description: "arch-variant srcs",
 			blueprint: `custom {
     name: "arch_paths",
@@ -470,7 +483,7 @@
 			android.FailIfErrored(t, err)
 
 			if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-				t.Errorf("Expected %d bazel target, got %d", expectedCount, actualCount)
+				t.Errorf("Expected %d bazel target (%s),\ngot %d (%s)", expectedCount, testCase.expectedBazelTargets, actualCount, bazelTargets)
 			} else {
 				for i, expectedBazelTarget := range testCase.expectedBazelTargets {
 					actualBazelTarget := bazelTargets[i]
@@ -596,6 +609,7 @@
 		{
 			bp: `custom {
     name: "bar",
+    one_to_many_prop: true,
     bazel_module: { bp2build_available: true  },
 }`,
 			expectedBazelTarget: `my_library(
@@ -620,7 +634,6 @@
 		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
 		ctx := android.NewTestContext(config)
 		ctx.RegisterModuleType("custom", customModuleFactory)
-		ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutatorFromStarlark)
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
@@ -658,10 +671,9 @@
 func TestModuleTypeBp2Build(t *testing.T) {
 	testCases := []bp2buildTestCase{
 		{
-			description:                        "filegroup with does not specify srcs",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "filegroup with does not specify srcs",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			blueprint: `filegroup {
     name: "fg_foo",
     bazel_module: { bp2build_available: true },
@@ -671,10 +683,9 @@
 			},
 		},
 		{
-			description:                        "filegroup with no srcs",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "filegroup with no srcs",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			blueprint: `filegroup {
     name: "fg_foo",
     srcs: [],
@@ -685,10 +696,9 @@
 			},
 		},
 		{
-			description:                        "filegroup with srcs",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "filegroup with srcs",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			blueprint: `filegroup {
     name: "fg_foo",
     srcs: ["a", "b"],
@@ -704,10 +714,9 @@
 			},
 		},
 		{
-			description:                        "filegroup with excludes srcs",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "filegroup with excludes srcs",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			blueprint: `filegroup {
     name: "fg_foo",
     srcs: ["a", "b"],
@@ -721,10 +730,9 @@
 			},
 		},
 		{
-			description:                        "filegroup with glob",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "filegroup with glob",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			blueprint: `filegroup {
     name: "fg_foo",
     srcs: ["**/*.txt"],
@@ -747,12 +755,10 @@
 			},
 		},
 		{
-			description:                        "filegroup with glob in subdir",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			blueprint:                          ``,
-			dir:                                "other",
+			description:                "filegroup with glob in subdir",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
+			dir:                        "other",
 			filesystem: map[string]string{
 				"other/Android.bp": `filegroup {
     name: "fg_foo",
@@ -775,10 +781,9 @@
 			},
 		},
 		{
-			description:                        "depends_on_other_dir_module",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "depends_on_other_dir_module",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			blueprint: `filegroup {
     name: "fg_foo",
     srcs: [
@@ -804,26 +809,25 @@
 			},
 		},
 		{
-			description:                        "depends_on_other_unconverted_module_error",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			unconvertedDepsMode:                errorModulesUnconvertedDeps,
-			filesystem: map[string]string{
-				"other/Android.bp": `filegroup {
-    name: "foo",
-    srcs: ["a", "b"],
-}`,
-			},
+			description:                "depends_on_other_unconverted_module_error",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
+			unconvertedDepsMode:        errorModulesUnconvertedDeps,
 			blueprint: `filegroup {
-    name: "fg_foo",
+    name: "foobar",
     srcs: [
         ":foo",
         "c",
     ],
     bazel_module: { bp2build_available: true },
 }`,
-			expectedErr: fmt.Errorf(`"fg_foo" depends on unconverted modules: foo`),
+			expectedErr: fmt.Errorf(`"foobar" depends on unconverted modules: foo`),
+			filesystem: map[string]string{
+				"other/Android.bp": `filegroup {
+    name: "foo",
+    srcs: ["a", "b"],
+}`,
+			},
 		},
 	}
 
@@ -838,18 +842,16 @@
 
 func TestAllowlistingBp2buildTargetsExplicitly(t *testing.T) {
 	testCases := []struct {
-		moduleTypeUnderTest                string
-		moduleTypeUnderTestFactory         android.ModuleFactory
-		moduleTypeUnderTestBp2BuildMutator bp2buildMutator
-		bp                                 string
-		expectedCount                      int
-		description                        string
+		moduleTypeUnderTest        string
+		moduleTypeUnderTestFactory android.ModuleFactory
+		bp                         string
+		expectedCount              int
+		description                string
 	}{
 		{
-			description:                        "explicitly unavailable",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "explicitly unavailable",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			bp: `filegroup {
     name: "foo",
     srcs: ["a", "b"],
@@ -858,10 +860,9 @@
 			expectedCount: 0,
 		},
 		{
-			description:                        "implicitly unavailable",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "implicitly unavailable",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			bp: `filegroup {
     name: "foo",
     srcs: ["a", "b"],
@@ -869,10 +870,9 @@
 			expectedCount: 0,
 		},
 		{
-			description:                        "explicitly available",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "explicitly available",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			bp: `filegroup {
     name: "foo",
     srcs: ["a", "b"],
@@ -881,12 +881,12 @@
 			expectedCount: 1,
 		},
 		{
-			description:                        "generates more than 1 target if needed",
-			moduleTypeUnderTest:                "custom",
-			moduleTypeUnderTestFactory:         customModuleFactory,
-			moduleTypeUnderTestBp2BuildMutator: customBp2BuildMutatorFromStarlark,
+			description:                "generates more than 1 target if needed",
+			moduleTypeUnderTest:        "custom",
+			moduleTypeUnderTestFactory: customModuleFactory,
 			bp: `custom {
     name: "foo",
+    one_to_many_prop: true,
     bazel_module: { bp2build_available: true },
 }`,
 			expectedCount: 3,
@@ -899,7 +899,6 @@
 			config := android.TestConfig(buildDir, nil, testCase.bp, nil)
 			ctx := android.NewTestContext(config)
 			ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-			ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
 			ctx.RegisterForBazelConversion()
 
 			_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
@@ -919,20 +918,18 @@
 
 func TestAllowlistingBp2buildTargetsWithConfig(t *testing.T) {
 	testCases := []struct {
-		moduleTypeUnderTest                string
-		moduleTypeUnderTestFactory         android.ModuleFactory
-		moduleTypeUnderTestBp2BuildMutator bp2buildMutator
-		expectedCount                      map[string]int
-		description                        string
-		bp2buildConfig                     android.Bp2BuildConfig
-		checkDir                           string
-		fs                                 map[string]string
+		moduleTypeUnderTest        string
+		moduleTypeUnderTestFactory android.ModuleFactory
+		expectedCount              map[string]int
+		description                string
+		bp2buildConfig             android.Bp2BuildConfig
+		checkDir                   string
+		fs                         map[string]string
 	}{
 		{
-			description:                        "test bp2build config package and subpackages config",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "test bp2build config package and subpackages config",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			expectedCount: map[string]int{
 				"migrated":                           1,
 				"migrated/but_not_really":            0,
@@ -954,10 +951,9 @@
 			},
 		},
 		{
-			description:                        "test bp2build config opt-in and opt-out",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "test bp2build config opt-in and opt-out",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			expectedCount: map[string]int{
 				"package-opt-in":             2,
 				"package-opt-in/subpackage":  0,
@@ -1008,7 +1004,6 @@
 		config := android.TestConfig(buildDir, nil, "", fs)
 		ctx := android.NewTestContext(config)
 		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
 		ctx.RegisterBp2BuildConfig(testCase.bp2buildConfig)
 		ctx.RegisterForBazelConversion()
 
@@ -1039,10 +1034,9 @@
 func TestCombineBuildFilesBp2buildTargets(t *testing.T) {
 	testCases := []bp2buildTestCase{
 		{
-			description:                        "filegroup bazel_module.label",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "filegroup bazel_module.label",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			blueprint: `filegroup {
     name: "fg_foo",
     bazel_module: { label: "//other:fg_foo" },
@@ -1055,19 +1049,18 @@
 			},
 		},
 		{
-			description:                        "multiple bazel_module.label same BUILD",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "multiple bazel_module.label same BUILD",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			blueprint: `filegroup {
-		    name: "fg_foo",
-		    bazel_module: { label: "//other:fg_foo" },
-		}
+        name: "fg_foo",
+        bazel_module: { label: "//other:fg_foo" },
+    }
 
-		filegroup {
-		    name: "foo",
-		    bazel_module: { label: "//other:foo" },
-		}`,
+    filegroup {
+        name: "foo",
+        bazel_module: { label: "//other:foo" },
+    }`,
 			expectedBazelTargets: []string{
 				`// BUILD file`,
 			},
@@ -1076,25 +1069,24 @@
 			},
 		},
 		{
-			description:                        "filegroup bazel_module.label and bp2build in subdir",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			dir:                                "other",
-			blueprint:                          ``,
+			description:                "filegroup bazel_module.label and bp2build in subdir",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
+			dir:                        "other",
+			blueprint:                  ``,
 			filesystem: map[string]string{
 				"other/Android.bp": `filegroup {
-				name: "fg_foo",
-				bazel_module: {
-					bp2build_available: true,
-				},
-			}
-			filegroup {
-				name: "fg_bar",
-				bazel_module: {
-					label: "//other:fg_bar"
-				},
-			}`,
+        name: "fg_foo",
+        bazel_module: {
+          bp2build_available: true,
+        },
+      }
+      filegroup {
+        name: "fg_bar",
+        bazel_module: {
+          label: "//other:fg_bar"
+        },
+      }`,
 				"other/BUILD.bazel": `// definition for fg_bar`,
 			},
 			expectedBazelTargets: []string{
@@ -1103,26 +1095,26 @@
 			},
 		},
 		{
-			description:                        "filegroup bazel_module.label and filegroup bp2build",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "filegroup bazel_module.label and filegroup bp2build",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
+
 			filesystem: map[string]string{
 				"other/BUILD.bazel": `// BUILD file`,
 			},
 			blueprint: `filegroup {
-		    name: "fg_foo",
-		    bazel_module: {
-		      label: "//other:fg_foo",
-		    },
-		}
+        name: "fg_foo",
+        bazel_module: {
+          label: "//other:fg_foo",
+        },
+    }
 
-		filegroup {
-		    name: "fg_bar",
-		    bazel_module: {
-		      bp2build_available: true,
-		    },
-		}`,
+    filegroup {
+        name: "fg_bar",
+        bazel_module: {
+          bp2build_available: true,
+        },
+    }`,
 			expectedBazelTargets: []string{
 				makeBazelTarget("filegroup", "fg_bar", map[string]string{}),
 				`// BUILD file`,
@@ -1146,7 +1138,6 @@
 			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)
@@ -1192,10 +1183,9 @@
 func TestGlobExcludeSrcs(t *testing.T) {
 	testCases := []bp2buildTestCase{
 		{
-			description:                        "filegroup top level exclude_srcs",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "filegroup top level exclude_srcs",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			blueprint: `filegroup {
     name: "fg_foo",
     srcs: ["**/*.txt"],
@@ -1222,12 +1212,11 @@
 			},
 		},
 		{
-			description:                        "filegroup in subdir exclude_srcs",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			blueprint:                          "",
-			dir:                                "dir",
+			description:                "filegroup in subdir exclude_srcs",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
+			blueprint:                  "",
+			dir:                        "dir",
 			filesystem: map[string]string{
 				"dir/Android.bp": `filegroup {
     name: "fg_foo",
@@ -1264,10 +1253,9 @@
 func TestCommonBp2BuildModuleAttrs(t *testing.T) {
 	testCases := []bp2buildTestCase{
 		{
-			description:                        "Required into data test",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "Required into data test",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			blueprint: simpleModuleDoNotConvertBp2build("filegroup", "reqd") + `
 filegroup {
     name: "fg_foo",
@@ -1281,21 +1269,20 @@
 			},
 		},
 		{
-			description:                        "Required via arch into data test",
-			moduleTypeUnderTest:                "python_library",
-			moduleTypeUnderTestFactory:         python.PythonLibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: python.PythonLibraryBp2Build,
+			description:                "Required via arch into data test",
+			moduleTypeUnderTest:        "python_library",
+			moduleTypeUnderTestFactory: python.PythonLibraryFactory,
 			blueprint: simpleModuleDoNotConvertBp2build("python_library", "reqdx86") +
 				simpleModuleDoNotConvertBp2build("python_library", "reqdarm") + `
 python_library {
     name: "fg_foo",
     arch: {
-			 arm: {
-				 required: ["reqdarm"],
-			 },
-			 x86: {
-				 required: ["reqdx86"],
-			 },
+       arm: {
+         required: ["reqdarm"],
+       },
+       x86: {
+         required: ["reqdx86"],
+       },
     },
     bazel_module: { bp2build_available: true },
 }`,
@@ -1311,10 +1298,9 @@
 			},
 		},
 		{
-			description:                        "Required appended to data test",
-			moduleTypeUnderTest:                "python_library",
-			moduleTypeUnderTestFactory:         python.PythonLibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: python.PythonLibraryBp2Build,
+			description:                "Required appended to data test",
+			moduleTypeUnderTest:        "python_library",
+			moduleTypeUnderTestFactory: python.PythonLibraryFactory,
 			filesystem: map[string]string{
 				"data.bin": "",
 				"src.py":   "",
@@ -1337,10 +1323,9 @@
 			},
 		},
 		{
-			description:                        "All props-to-attrs at once together test",
-			moduleTypeUnderTest:                "filegroup",
-			moduleTypeUnderTestFactory:         android.FileGroupFactory,
-			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			description:                "All props-to-attrs at once together test",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
 			blueprint: simpleModuleDoNotConvertBp2build("filegroup", "reqd") + `
 filegroup {
     name: "fg_foo",
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 1e78c0e..f3345a6 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -100,6 +100,7 @@
         # nested_props_ptr start
 #         "nested_prop": attr.string(),
         # nested_props_ptr end
+        "one_to_many_prop": attr.bool(),
         "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
         "string_prop": attr.string(),
@@ -128,6 +129,7 @@
         # nested_props_ptr start
 #         "nested_prop": attr.string(),
         # nested_props_ptr end
+        "one_to_many_prop": attr.bool(),
         "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
         "string_prop": attr.string(),
@@ -156,6 +158,7 @@
         # nested_props_ptr start
 #         "nested_prop": attr.string(),
         # nested_props_ptr end
+        "one_to_many_prop": attr.bool(),
         "other_embedded_prop": attr.string(),
         "string_list_prop": attr.string_list(),
         "string_prop": attr.string(),
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index f9abcba..8d94079 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -24,8 +24,7 @@
 )
 
 const (
-	ccBinaryTypePlaceHolder   = "{rule_name}"
-	compatibleWithPlaceHolder = "{target_compatible_with}"
+	ccBinaryTypePlaceHolder = "{rule_name}"
 )
 
 type testBazelTarget struct {
@@ -69,14 +68,14 @@
 	t.Helper()
 	moduleTypeUnderTest := "cc_binary"
 	testCase := bp2buildTestCase{
-		expectedBazelTargets:               generateBazelTargetsForTest(tc.targets),
-		moduleTypeUnderTest:                moduleTypeUnderTest,
-		moduleTypeUnderTestFactory:         cc.BinaryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.BinaryBp2build,
-		description:                        fmt.Sprintf("%s %s", moduleTypeUnderTest, tc.description),
-		blueprint:                          binaryReplacer.Replace(tc.blueprint),
+		expectedBazelTargets:       generateBazelTargetsForTest(tc.targets),
+		moduleTypeUnderTest:        moduleTypeUnderTest,
+		moduleTypeUnderTestFactory: cc.BinaryFactory,
+		description:                fmt.Sprintf("%s %s", moduleTypeUnderTest, tc.description),
+		blueprint:                  binaryReplacer.Replace(tc.blueprint),
 	}
 	t.Run(testCase.description, func(t *testing.T) {
+		t.Helper()
 		runBp2BuildTestCase(t, registerCcBinaryModuleTypes, testCase)
 	})
 }
@@ -84,22 +83,24 @@
 func runCcHostBinaryTestCase(t *testing.T, tc ccBinaryBp2buildTestCase) {
 	t.Helper()
 	testCase := tc
-	for i, t := range testCase.targets {
-		t.attrs["target_compatible_with"] = `select({
+	for i, tar := range testCase.targets {
+		switch tar.typ {
+		case "cc_binary", "proto_library", "cc_lite_proto_library":
+			tar.attrs["target_compatible_with"] = `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
     })`
-		testCase.targets[i] = t
+		}
+		testCase.targets[i] = tar
 	}
 	moduleTypeUnderTest := "cc_binary_host"
 	t.Run(testCase.description, func(t *testing.T) {
 		runBp2BuildTestCase(t, registerCcBinaryModuleTypes, bp2buildTestCase{
-			expectedBazelTargets:               generateBazelTargetsForTest(testCase.targets),
-			moduleTypeUnderTest:                moduleTypeUnderTest,
-			moduleTypeUnderTestFactory:         cc.BinaryHostFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.BinaryHostBp2build,
-			description:                        fmt.Sprintf("%s %s", moduleTypeUnderTest, tc.description),
-			blueprint:                          hostBinaryReplacer.Replace(testCase.blueprint),
+			expectedBazelTargets:       generateBazelTargetsForTest(testCase.targets),
+			moduleTypeUnderTest:        moduleTypeUnderTest,
+			moduleTypeUnderTestFactory: cc.BinaryHostFactory,
+			description:                fmt.Sprintf("%s %s", moduleTypeUnderTest, tc.description),
+			blueprint:                  hostBinaryReplacer.Replace(testCase.blueprint),
 		})
 	})
 }
@@ -256,11 +257,13 @@
 genrule {
     name: "generated_hdr",
     cmd: "nothing to see here",
+    bazel_module: { bp2build_available: false },
 }
 
 genrule {
     name: "export_generated_hdr",
     cmd: "nothing to see here",
+    bazel_module: { bp2build_available: false },
 }
 
 {rule_name} {
@@ -301,6 +304,7 @@
         ":not_explicitly_exported_whole_static_dep",
         ":whole_static_dep",
     ]`,
+				"local_includes": `["."]`,
 			},
 			},
 		},
@@ -448,3 +452,49 @@
 		})
 	}
 }
+
+func TestCcBinarySharedProto(t *testing.T) {
+	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+		blueprint: soongCcProtoLibraries + `{rule_name} {
+	name: "foo",
+	srcs: ["foo.proto"],
+	proto: {
+	},
+	include_build_directory: false,
+}`,
+		targets: []testBazelTarget{
+			{"proto_library", "foo_proto", attrNameToString{
+				"srcs": `["foo.proto"]`,
+			}}, {"cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+				"deps": `[":foo_proto"]`,
+			}}, {"cc_binary", "foo", attrNameToString{
+				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":foo_cc_proto_lite"]`,
+			}},
+		},
+	})
+}
+
+func TestCcBinaryStaticProto(t *testing.T) {
+	runCcBinaryTests(t, ccBinaryBp2buildTestCase{
+		blueprint: soongCcProtoLibraries + `{rule_name} {
+	name: "foo",
+	srcs: ["foo.proto"],
+	static_executable: true,
+	proto: {
+	},
+	include_build_directory: false,
+}`,
+		targets: []testBazelTarget{
+			{"proto_library", "foo_proto", attrNameToString{
+				"srcs": `["foo.proto"]`,
+			}}, {"cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+				"deps": `[":foo_proto"]`,
+			}}, {"cc_binary", "foo", attrNameToString{
+				"deps":               `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":foo_cc_proto_lite"]`,
+				"linkshared":         `False`,
+			}},
+		},
+	})
+}
diff --git a/bp2build/cc_genrule_conversion_test.go b/bp2build/cc_genrule_conversion_test.go
deleted file mode 100644
index b3624dd..0000000
--- a/bp2build/cc_genrule_conversion_test.go
+++ /dev/null
@@ -1,237 +0,0 @@
-// 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"
-	"android/soong/genrule"
-)
-
-var otherCcGenruleBp = map[string]string{
-	"other/Android.bp": `cc_genrule {
-    name: "foo.tool",
-    out: ["foo_tool.out"],
-    srcs: ["foo_tool.in"],
-    cmd: "cp $(in) $(out)",
-}
-cc_genrule {
-    name: "other.tool",
-    out: ["other_tool.out"],
-    srcs: ["other_tool.in"],
-    cmd: "cp $(in) $(out)",
-}`,
-}
-
-func runCcGenruleTestCase(t *testing.T, tc bp2buildTestCase) {
-	t.Helper()
-	(&tc).moduleTypeUnderTest = "cc_genrule"
-	(&tc).moduleTypeUnderTestFactory = cc.GenRuleFactory
-	(&tc).moduleTypeUnderTestBp2BuildMutator = genrule.CcGenruleBp2Build
-	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
-}
-
-func TestCliVariableReplacement(t *testing.T) {
-	runCcGenruleTestCase(t, bp2buildTestCase{
-		description: "cc_genrule with command line variable replacements",
-		blueprint: `cc_genrule {
-    name: "foo.tool",
-    out: ["foo_tool.out"],
-    srcs: ["foo_tool.in"],
-    cmd: "cp $(in) $(out)",
-    bazel_module: { bp2build_available: true },
-}
-
-cc_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{
-			makeBazelTarget("genrule", "foo", attrNameToString{
-				"cmd":   `"$(location :foo.tool) --genDir=$(RULEDIR) arg $(SRCS) $(OUTS)"`,
-				"outs":  `["foo.out"]`,
-				"srcs":  `["foo.in"]`,
-				"tools": `[":foo.tool"]`,
-			}),
-			makeBazelTarget("genrule", "foo.tool", attrNameToString{
-				"cmd":  `"cp $(SRCS) $(OUTS)"`,
-				"outs": `["foo_tool.out"]`,
-				"srcs": `["foo_tool.in"]`,
-			}),
-		},
-	})
-}
-
-func TestUsingLocationsLabel(t *testing.T) {
-	runCcGenruleTestCase(t, bp2buildTestCase{
-		description: "cc_genrule using $(locations :label)",
-		blueprint: `cc_genrule {
-    name: "foo.tools",
-    out: ["foo_tool.out", "foo_tool2.out"],
-    srcs: ["foo_tool.in"],
-    cmd: "cp $(in) $(out)",
-    bazel_module: { bp2build_available: true },
-}
-
-cc_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{
-			makeBazelTarget("genrule", "foo", attrNameToString{
-				"cmd":   `"$(locations :foo.tools) -s $(OUTS) $(SRCS)"`,
-				"outs":  `["foo.out"]`,
-				"srcs":  `["foo.in"]`,
-				"tools": `[":foo.tools"]`,
-			}),
-			makeBazelTarget("genrule", "foo.tools", attrNameToString{
-				"cmd": `"cp $(SRCS) $(OUTS)"`,
-				"outs": `[
-        "foo_tool.out",
-        "foo_tool2.out",
-    ]`,
-				"srcs": `["foo_tool.in"]`,
-			}),
-		},
-	})
-}
-
-func TestUsingLocationsAbsoluteLabel(t *testing.T) {
-	runCcGenruleTestCase(t, bp2buildTestCase{
-		description: "cc_genrule using $(locations //absolute:label)",
-		blueprint: `cc_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 },
-}`,
-		filesystem: otherCcGenruleBp,
-		expectedBazelTargets: []string{
-			makeBazelTarget("genrule", "foo", attrNameToString{
-				"cmd":   `"$(locations //other:foo.tool) -s $(OUTS) $(SRCS)"`,
-				"outs":  `["foo.out"]`,
-				"srcs":  `["foo.in"]`,
-				"tools": `["//other:foo.tool"]`,
-			}),
-		},
-	})
-}
-
-func TestSrcsUsingAbsoluteLabel(t *testing.T) {
-	runCcGenruleTestCase(t, bp2buildTestCase{
-		description: "cc_genrule srcs using $(locations //absolute:label)",
-		blueprint: `cc_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 },
-}`,
-		filesystem: otherCcGenruleBp,
-		expectedBazelTargets: []string{
-			makeBazelTarget("genrule", "foo", attrNameToString{
-				"cmd":   `"$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)"`,
-				"outs":  `["foo.out"]`,
-				"srcs":  `["//other:other.tool"]`,
-				"tools": `["//other:foo.tool"]`,
-			}),
-		},
-	})
-}
-
-func TestLocationsLabelUsesFirstToolFile(t *testing.T) {
-	runCcGenruleTestCase(t, bp2buildTestCase{
-		description: "cc_genrule using $(location) label should substitute first tool label automatically",
-		blueprint: `cc_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 },
-}`,
-		filesystem: otherCcGenruleBp,
-		expectedBazelTargets: []string{
-			makeBazelTarget("genrule", "foo", attrNameToString{
-				"cmd":  `"$(location //other:foo.tool) -s $(OUTS) $(SRCS)"`,
-				"outs": `["foo.out"]`,
-				"srcs": `["foo.in"]`,
-				"tools": `[
-        "//other:foo.tool",
-        "//other:other.tool",
-    ]`,
-			}),
-		},
-	})
-}
-
-func TestLocationsLabelUsesFirstTool(t *testing.T) {
-	runCcGenruleTestCase(t, bp2buildTestCase{
-		description: "cc_genrule using $(locations) label should substitute first tool label automatically",
-		blueprint: `cc_genrule {
-    name: "foo",
-    out: ["foo.out"],
-    srcs: ["foo.in"],
-    tools: [":foo.tool", ":other.tool"],
-    cmd: "$(locations) -s $(out) $(in)",
-    bazel_module: { bp2build_available: true },
-}`,
-		filesystem: otherCcGenruleBp,
-		expectedBazelTargets: []string{
-			makeBazelTarget("genrule", "foo", attrNameToString{
-				"cmd":  `"$(locations //other:foo.tool) -s $(OUTS) $(SRCS)"`,
-				"outs": `["foo.out"]`,
-				"srcs": `["foo.in"]`,
-				"tools": `[
-        "//other:foo.tool",
-        "//other:other.tool",
-    ]`,
-			}),
-		},
-	})
-}
-
-func TestWithoutToolsOrToolFiles(t *testing.T) {
-	runCcGenruleTestCase(t, bp2buildTestCase{
-		description: "cc_genrule without tools or tool_files can convert successfully",
-		blueprint: `cc_genrule {
-    name: "foo",
-    out: ["foo.out"],
-    srcs: ["foo.in"],
-    cmd: "cp $(in) $(out)",
-    bazel_module: { bp2build_available: true },
-}`,
-		expectedBazelTargets: []string{
-			makeBazelTarget("genrule", "foo", attrNameToString{
-				"cmd":  `"cp $(SRCS) $(OUTS)"`,
-				"outs": `["foo.out"]`,
-				"srcs": `["foo.in"]`,
-			}),
-		},
-	})
-}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index d23ea01..8fde655 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -27,18 +27,20 @@
 	soongCcLibraryPreamble = `
 cc_defaults {
     name: "linux_bionic_supported",
+}`
+
+	soongCcProtoLibraries = `
+cc_library {
+	name: "libprotobuf-cpp-lite",
+	bazel_module: { bp2build_available: false },
 }
 
-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: "",
+cc_library {
+	name: "libprotobuf-cpp-full",
+	bazel_module: { bp2build_available: false },
 }`
+
+	soongCcProtoPreamble = soongCcLibraryPreamble + soongCcProtoLibraries
 )
 
 func runCcLibraryTestCase(t *testing.T, tc bp2buildTestCase) {
@@ -51,16 +53,14 @@
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
 	ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory)
-	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
 	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
 }
 
 func TestCcLibrarySimple(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library - simple example",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library - simple example",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		filesystem: map[string]string{
 			"android.cpp": "",
 			"bionic.cpp":  "",
@@ -117,17 +117,16 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "foo-lib", attrNameToString{
-				"copts":               `["-Wall"]`,
-				"export_includes":     `["foo-dir"]`,
-				"implementation_deps": `[":some-headers"]`,
-				"linkopts": `["-Wl,--exclude-libs=bar.a"] + select({
+		expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+			"copts":               `["-Wall"]`,
+			"export_includes":     `["foo-dir"]`,
+			"implementation_deps": `[":some-headers"]`,
+			"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"],
         "//conditions:default": [],
     })`,
-				"srcs": `["impl.cpp"] + select({
+			"srcs": `["impl.cpp"] + select({
         "//build/bazel/platforms/arch:x86": ["x86.cpp"],
         "//build/bazel/platforms/arch:x86_64": ["x86_64.cpp"],
         "//conditions:default": [],
@@ -141,17 +140,15 @@
         "//build/bazel/platforms/os:linux_bionic": ["bionic.cpp"],
         "//conditions:default": [],
     })`,
-			}),
-		},
+		}),
 	})
 }
 
 func TestCcLibraryTrimmedLdAndroid(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library - trimmed example of //bionic/linker:ld-android",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library - trimmed example of //bionic/linker:ld-android",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		filesystem: map[string]string{
 			"ld-android.cpp":           "",
 			"linked_list.h":            "",
@@ -159,8 +156,8 @@
 			"linker_block_allocator.h": "",
 			"linker_cfi.h":             "",
 		},
-		blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "libc_headers" }
+		blueprint: soongCcLibraryPreamble +
+			simpleModuleDoNotConvertBp2build("cc_library_headers", "libc_headers") + `
 cc_library {
     name: "fake-ld-android",
     srcs: ["ld_android.cpp"],
@@ -190,17 +187,16 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "fake-ld-android", attrNameToString{
-				"srcs": `["ld_android.cpp"]`,
-				"copts": `[
+		expectedBazelTargets: makeCcLibraryTargets("fake-ld-android", attrNameToString{
+			"srcs": `["ld_android.cpp"]`,
+			"copts": `[
         "-Wall",
         "-Wextra",
         "-Wunused",
         "-Werror",
     ]`,
-				"implementation_deps": `[":libc_headers"]`,
-				"linkopts": `[
+			"implementation_deps": `[":libc_headers"]`,
+			"linkopts": `[
         "-Wl,--exclude-libs=libgcc.a",
         "-Wl,--exclude-libs=libgcc_stripped.a",
         "-Wl,--exclude-libs=libclang_rt.builtins-arm-android.a",
@@ -212,18 +208,16 @@
         "//build/bazel/platforms/arch:x86_64": ["-Wl,--exclude-libs=libgcc_eh.a"],
         "//conditions:default": [],
     })`,
-			}),
-		},
+		}),
 	})
 }
 
 func TestCcLibraryExcludeSrcs(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library exclude_srcs - trimmed example of //external/arm-optimized-routines:libarm-optimized-routines-math",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "external",
+		description:                "cc_library exclude_srcs - trimmed example of //external/arm-optimized-routines:libarm-optimized-routines-math",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		dir:                        "external",
 		filesystem: map[string]string{
 			"external/math/cosf.c":      "",
 			"external/math/erf.c":       "",
@@ -258,25 +252,22 @@
 `,
 		},
 		blueprint: soongCcLibraryPreamble,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "fake-libarm-optimized-routines-math", attrNameToString{
-				"copts": `select({
+		expectedBazelTargets: makeCcLibraryTargets("fake-libarm-optimized-routines-math", attrNameToString{
+			"copts": `select({
         "//build/bazel/platforms/arch:arm64": ["-DHAVE_FAST_FMA=1"],
         "//conditions:default": [],
     })`,
-				"local_includes": `["."]`,
-				"srcs_c":         `["math/cosf.c"]`,
-			}),
-		},
+			"local_includes": `["."]`,
+			"srcs_c":         `["math/cosf.c"]`,
+		}),
 	})
 }
 
 func TestCcLibrarySharedStaticProps(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library shared/static props",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library shared/static props",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		filesystem: map[string]string{
 			"both.cpp":       "",
 			"sharedonly.cpp": "",
@@ -353,26 +344,48 @@
 }
 `,
 		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "a", attrNameToString{
-				"copts":                       `["bothflag"]`,
-				"implementation_deps":         `[":static_dep_for_both"]`,
-				"implementation_dynamic_deps": `[":shared_dep_for_both"]`,
-				"shared": `{
-        "copts": ["sharedflag"],
-        "implementation_deps": [":static_dep_for_shared"],
-        "implementation_dynamic_deps": [":shared_dep_for_shared"],
-        "srcs": ["sharedonly.cpp"],
-        "whole_archive_deps": [":whole_static_lib_for_shared"],
-    }`,
-				"srcs": `["both.cpp"]`,
-				"static": `{
-        "copts": ["staticflag"],
-        "implementation_deps": [":static_dep_for_static"],
-        "implementation_dynamic_deps": [":shared_dep_for_static"],
-        "srcs": ["staticonly.cpp"],
-        "whole_archive_deps": [":whole_static_lib_for_static"],
-    }`,
-				"whole_archive_deps": `[":whole_static_lib_for_both"]`,
+			makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+				"copts": `[
+        "bothflag",
+        "staticflag",
+    ]`,
+				"implementation_deps": `[
+        ":static_dep_for_both",
+        ":static_dep_for_static",
+    ]`,
+				"implementation_dynamic_deps": `[
+        ":shared_dep_for_both",
+        ":shared_dep_for_static",
+    ]`,
+				"srcs": `[
+        "both.cpp",
+        "staticonly.cpp",
+    ]`,
+				"whole_archive_deps": `[
+        ":whole_static_lib_for_both",
+        ":whole_static_lib_for_static",
+    ]`}),
+			makeBazelTarget("cc_library_shared", "a", attrNameToString{
+				"copts": `[
+        "bothflag",
+        "sharedflag",
+    ]`,
+				"implementation_deps": `[
+        ":static_dep_for_both",
+        ":static_dep_for_shared",
+    ]`,
+				"implementation_dynamic_deps": `[
+        ":shared_dep_for_both",
+        ":shared_dep_for_shared",
+    ]`,
+				"srcs": `[
+        "both.cpp",
+        "sharedonly.cpp",
+    ]`,
+				"whole_archive_deps": `[
+        ":whole_static_lib_for_both",
+        ":whole_static_lib_for_shared",
+    ]`,
 			}),
 		},
 	})
@@ -380,10 +393,9 @@
 
 func TestCcLibraryDeps(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library shared/static props",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library shared/static props",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		filesystem: map[string]string{
 			"both.cpp":       "",
 			"sharedonly.cpp": "",
@@ -438,52 +450,79 @@
 			simpleModuleDoNotConvertBp2build("cc_library", "shared_dep_for_both") +
 			simpleModuleDoNotConvertBp2build("cc_library", "implementation_shared_dep_for_both"),
 		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "a", attrNameToString{
-				"copts":                       `["bothflag"]`,
-				"deps":                        `[":static_dep_for_both"]`,
-				"dynamic_deps":                `[":shared_dep_for_both"]`,
-				"implementation_deps":         `[":implementation_static_dep_for_both"]`,
-				"implementation_dynamic_deps": `[":implementation_shared_dep_for_both"]`,
-				"shared": `{
-        "copts": ["sharedflag"],
-        "deps": [":static_dep_for_shared"],
-        "dynamic_deps": [":shared_dep_for_shared"],
-        "implementation_deps": [":implementation_static_dep_for_shared"],
-        "implementation_dynamic_deps": [":implementation_shared_dep_for_shared"],
-        "srcs": ["sharedonly.cpp"],
-        "whole_archive_deps": [
-            ":not_explicitly_exported_whole_static_dep_for_shared",
-            ":whole_static_dep_for_shared",
-        ],
-    }`,
-				"srcs": `["both.cpp"]`,
-				"static": `{
-        "copts": ["staticflag"],
-        "deps": [":static_dep_for_static"],
-        "dynamic_deps": [":shared_dep_for_static"],
-        "implementation_deps": [":implementation_static_dep_for_static"],
-        "implementation_dynamic_deps": [":implementation_shared_dep_for_static"],
-        "srcs": ["staticonly.cpp"],
-        "whole_archive_deps": [
-            ":not_explicitly_exported_whole_static_dep_for_static",
-            ":whole_static_dep_for_static",
-        ],
-    }`,
+			makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+				"copts": `[
+        "bothflag",
+        "staticflag",
+    ]`,
+				"deps": `[
+        ":static_dep_for_both",
+        ":static_dep_for_static",
+    ]`,
+				"dynamic_deps": `[
+        ":shared_dep_for_both",
+        ":shared_dep_for_static",
+    ]`,
+				"implementation_deps": `[
+        ":implementation_static_dep_for_both",
+        ":implementation_static_dep_for_static",
+    ]`,
+				"implementation_dynamic_deps": `[
+        ":implementation_shared_dep_for_both",
+        ":implementation_shared_dep_for_static",
+    ]`,
+				"srcs": `[
+        "both.cpp",
+        "staticonly.cpp",
+    ]`,
 				"whole_archive_deps": `[
         ":not_explicitly_exported_whole_static_dep_for_both",
         ":whole_static_dep_for_both",
+        ":not_explicitly_exported_whole_static_dep_for_static",
+        ":whole_static_dep_for_static",
     ]`,
 			}),
-		},
-	})
+			makeBazelTarget("cc_library_shared", "a", attrNameToString{
+				"copts": `[
+        "bothflag",
+        "sharedflag",
+    ]`,
+				"deps": `[
+        ":static_dep_for_both",
+        ":static_dep_for_shared",
+    ]`,
+				"dynamic_deps": `[
+        ":shared_dep_for_both",
+        ":shared_dep_for_shared",
+    ]`,
+				"implementation_deps": `[
+        ":implementation_static_dep_for_both",
+        ":implementation_static_dep_for_shared",
+    ]`,
+				"implementation_dynamic_deps": `[
+        ":implementation_shared_dep_for_both",
+        ":implementation_shared_dep_for_shared",
+    ]`,
+				"srcs": `[
+        "both.cpp",
+        "sharedonly.cpp",
+    ]`,
+				"whole_archive_deps": `[
+        ":not_explicitly_exported_whole_static_dep_for_both",
+        ":whole_static_dep_for_both",
+        ":not_explicitly_exported_whole_static_dep_for_shared",
+        ":whole_static_dep_for_shared",
+    ]`,
+			})},
+	},
+	)
 }
 
 func TestCcLibraryWholeStaticLibsAlwaysLink(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		dir:                        "foo/bar",
 		filesystem: map[string]string{
 			"foo/bar/Android.bp": `
 cc_library {
@@ -508,26 +547,29 @@
 		},
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "a", attrNameToString{
-				"shared": `{
-        "whole_archive_deps": [":whole_static_lib_for_shared_alwayslink"],
-    }`,
-				"static": `{
-        "whole_archive_deps": [":whole_static_lib_for_static_alwayslink"],
-    }`,
-				"whole_archive_deps": `[":whole_static_lib_for_both_alwayslink"]`,
+			makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+				"whole_archive_deps": `[
+        ":whole_static_lib_for_both_alwayslink",
+        ":whole_static_lib_for_static_alwayslink",
+    ]`,
+			}),
+			makeBazelTarget("cc_library_shared", "a", attrNameToString{
+				"whole_archive_deps": `[
+        ":whole_static_lib_for_both_alwayslink",
+        ":whole_static_lib_for_shared_alwayslink",
+    ]`,
 			}),
 		},
-	})
+	},
+	)
 }
 
 func TestCcLibrarySharedStaticPropsInArch(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library shared/static props in arch",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
+		description:                "cc_library shared/static props in arch",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		dir:                        "foo/bar",
 		filesystem: map[string]string{
 			"foo/bar/arm.cpp":        "",
 			"foo/bar/x86.cpp":        "",
@@ -599,71 +641,85 @@
 		},
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "a", attrNameToString{
-				"copts":               `["bothflag"]`,
-				"implementation_deps": `[":static_dep_for_both"]`,
-				"local_includes":      `["."]`,
-				"shared": `{
-        "copts": ["sharedflag"] + select({
-            "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"],
-            "//conditions:default": [],
-        }) + select({
-            "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"],
-            "//conditions:default": [],
-        }) + select({
-            "//build/bazel/platforms/os_arch:android_arm": ["-DANDROID_ARM_SHARED"],
-            "//conditions:default": [],
-        }),
-        "implementation_deps": [":static_dep_for_shared"] + select({
-            "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"],
-            "//conditions:default": [],
-        }) + select({
-            "//build/bazel/platforms/os:android": [":android_dep_for_shared"],
-            "//conditions:default": [],
-        }),
-        "implementation_dynamic_deps": select({
-            "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"],
-            "//conditions:default": [],
-        }),
-        "srcs": ["sharedonly.cpp"] + select({
-            "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"],
-            "//conditions:default": [],
-        }) + select({
-            "//build/bazel/platforms/os:android": ["android_shared.cpp"],
-            "//conditions:default": [],
-        }),
-        "whole_archive_deps": select({
-            "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"],
-            "//conditions:default": [],
-        }),
-    }`,
-				"srcs": `["both.cpp"]`,
-				"static": `{
-        "copts": ["staticflag"] + select({
-            "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"],
-            "//conditions:default": [],
-        }),
-        "implementation_deps": [":static_dep_for_static"] + select({
-            "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"],
-            "//conditions:default": [],
-        }),
-        "srcs": ["staticonly.cpp"] + select({
-            "//build/bazel/platforms/arch:x86": ["x86_static.cpp"],
-            "//conditions:default": [],
-        }),
-    }`,
+			makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+				"copts": `[
+        "bothflag",
+        "staticflag",
+    ] + select({
+        "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"],
+        "//conditions:default": [],
+    })`,
+				"implementation_deps": `[
+        ":static_dep_for_both",
+        ":static_dep_for_static",
+    ] + select({
+        "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+				"srcs": `[
+        "both.cpp",
+        "staticonly.cpp",
+    ] + select({
+        "//build/bazel/platforms/arch:x86": ["x86_static.cpp"],
+        "//conditions:default": [],
+    })`,
+			}),
+			makeBazelTarget("cc_library_shared", "a", attrNameToString{
+				"copts": `[
+        "bothflag",
+        "sharedflag",
+    ] + select({
+        "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os_arch:android_arm": ["-DANDROID_ARM_SHARED"],
+        "//conditions:default": [],
+    })`,
+				"implementation_deps": `[
+        ":static_dep_for_both",
+        ":static_dep_for_shared",
+    ] + select({
+        "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:android": [":android_dep_for_shared"],
+        "//conditions:default": [],
+    })`,
+				"implementation_dynamic_deps": `select({
+        "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+				"srcs": `[
+        "both.cpp",
+        "sharedonly.cpp",
+    ] + select({
+        "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:android": ["android_shared.cpp"],
+        "//conditions:default": [],
+    })`,
+				"whole_archive_deps": `select({
+        "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"],
+        "//conditions:default": [],
+    })`,
 			}),
 		},
-	})
+	},
+	)
 }
 
 func TestCcLibrarySharedStaticPropsWithMixedSources(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library shared/static props with c/cpp/s mixed sources",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
+		description:                "cc_library shared/static props with c/cpp/s mixed sources",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		dir:                        "foo/bar",
 		filesystem: map[string]string{
 			"foo/bar/both_source.cpp":   "",
 			"foo/bar/both_source.cc":    "",
@@ -738,66 +794,64 @@
 		},
 		blueprint: soongCcLibraryPreamble,
 		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "a", attrNameToString{
+			makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
 				"local_includes": `["."]`,
-				"shared": `{
-        "srcs": [
-            "shared_source.cpp",
-            "shared_source.cc",
-            ":shared_filegroup_cpp_srcs",
-        ],
-        "srcs_as": [
-            "shared_source.s",
-            "shared_source.S",
-            ":shared_filegroup_as_srcs",
-        ],
-        "srcs_c": [
-            "shared_source.c",
-            ":shared_filegroup_c_srcs",
-        ],
-    }`,
 				"srcs": `[
         "both_source.cpp",
         "both_source.cc",
         ":both_filegroup_cpp_srcs",
+        "static_source.cpp",
+        "static_source.cc",
+        ":static_filegroup_cpp_srcs",
     ]`,
 				"srcs_as": `[
         "both_source.s",
         "both_source.S",
         ":both_filegroup_as_srcs",
+        "static_source.s",
+        "static_source.S",
+        ":static_filegroup_as_srcs",
     ]`,
 				"srcs_c": `[
         "both_source.c",
         ":both_filegroup_c_srcs",
+        "static_source.c",
+        ":static_filegroup_c_srcs",
     ]`,
-				"static": `{
-        "srcs": [
-            "static_source.cpp",
-            "static_source.cc",
-            ":static_filegroup_cpp_srcs",
-        ],
-        "srcs_as": [
-            "static_source.s",
-            "static_source.S",
-            ":static_filegroup_as_srcs",
-        ],
-        "srcs_c": [
-            "static_source.c",
-            ":static_filegroup_c_srcs",
-        ],
-    }`,
 			}),
-		},
-	})
+			makeBazelTarget("cc_library_shared", "a", attrNameToString{
+				"local_includes": `["."]`,
+				"srcs": `[
+        "both_source.cpp",
+        "both_source.cc",
+        ":both_filegroup_cpp_srcs",
+        "shared_source.cpp",
+        "shared_source.cc",
+        ":shared_filegroup_cpp_srcs",
+    ]`,
+				"srcs_as": `[
+        "both_source.s",
+        "both_source.S",
+        ":both_filegroup_as_srcs",
+        "shared_source.s",
+        "shared_source.S",
+        ":shared_filegroup_as_srcs",
+    ]`,
+				"srcs_c": `[
+        "both_source.c",
+        ":both_filegroup_c_srcs",
+        "shared_source.c",
+        ":shared_filegroup_c_srcs",
+    ]`,
+			})}})
 }
 
 func TestCcLibraryNonConfiguredVersionScript(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library non-configured version script",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
+		description:                "cc_library non-configured version script",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		dir:                        "foo/bar",
 		filesystem: map[string]string{
 			"foo/bar/Android.bp": `
 cc_library {
@@ -810,23 +864,21 @@
 `,
 		},
 		blueprint: soongCcLibraryPreamble,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "a", attrNameToString{
-				"additional_linker_inputs": `["v.map"]`,
-				"linkopts":                 `["-Wl,--version-script,$(location v.map)"]`,
-				"srcs":                     `["a.cpp"]`,
-			}),
-		},
-	})
+		expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+			"additional_linker_inputs": `["v.map"]`,
+			"linkopts":                 `["-Wl,--version-script,$(location v.map)"]`,
+			"srcs":                     `["a.cpp"]`,
+		}),
+	},
+	)
 }
 
 func TestCcLibraryConfiguredVersionScript(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library configured version script",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		dir:                                "foo/bar",
+		description:                "cc_library configured version script",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		dir:                        "foo/bar",
 		filesystem: map[string]string{
 			"foo/bar/Android.bp": `
 cc_library {
@@ -847,30 +899,28 @@
     `,
 		},
 		blueprint: soongCcLibraryPreamble,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "a", attrNameToString{
-				"additional_linker_inputs": `select({
+		expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+			"additional_linker_inputs": `select({
         "//build/bazel/platforms/arch:arm": ["arm.map"],
         "//build/bazel/platforms/arch:arm64": ["arm64.map"],
         "//conditions:default": [],
     })`,
-				"linkopts": `select({
+			"linkopts": `select({
         "//build/bazel/platforms/arch:arm": ["-Wl,--version-script,$(location arm.map)"],
         "//build/bazel/platforms/arch:arm64": ["-Wl,--version-script,$(location arm64.map)"],
         "//conditions:default": [],
     })`,
-				"srcs": `["a.cpp"]`,
-			}),
-		},
-	})
+			"srcs": `["a.cpp"]`,
+		}),
+	},
+	)
 }
 
 func TestCcLibrarySharedLibs(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library shared_libs",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library shared_libs",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "mylib",
@@ -883,20 +933,47 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "a", attrNameToString{
-				"implementation_dynamic_deps": `[":mylib"]`,
-			}),
-		},
-	})
+		expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+			"implementation_dynamic_deps": `[":mylib"]`,
+		}),
+	},
+	)
 }
 
 func TestCcLibraryFeatures(t *testing.T) {
+	expected_targets := []string{}
+	expected_targets = append(expected_targets, makeCcLibraryTargets("a", attrNameToString{
+		"features": `[
+        "disable_pack_relocations",
+        "-no_undefined_symbols",
+    ]`,
+		"srcs": `["a.cpp"]`,
+	})...)
+	expected_targets = append(expected_targets, makeCcLibraryTargets("b", attrNameToString{
+		"features": `select({
+        "//build/bazel/platforms/arch:x86_64": [
+            "disable_pack_relocations",
+            "-no_undefined_symbols",
+        ],
+        "//conditions:default": [],
+    })`,
+		"srcs": `["b.cpp"]`,
+	})...)
+	expected_targets = append(expected_targets, makeCcLibraryTargets("c", attrNameToString{
+		"features": `select({
+        "//build/bazel/platforms/os:darwin": [
+            "disable_pack_relocations",
+            "-no_undefined_symbols",
+        ],
+        "//conditions:default": [],
+    })`,
+		"srcs": `["c.cpp"]`,
+	})...)
+
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library pack_relocations test",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library pack_relocations test",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "a",
@@ -929,42 +1006,15 @@
     },
     include_build_directory: false,
 }`,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "a", attrNameToString{
-				"features": `[
-        "disable_pack_relocations",
-        "-no_undefined_symbols",
-    ]`,
-				"srcs": `["a.cpp"]`,
-			}), makeBazelTarget("cc_library", "b", attrNameToString{
-				"features": `select({
-        "//build/bazel/platforms/arch:x86_64": [
-            "disable_pack_relocations",
-            "-no_undefined_symbols",
-        ],
-        "//conditions:default": [],
-    })`,
-				"srcs": `["b.cpp"]`,
-			}), makeBazelTarget("cc_library", "c", attrNameToString{
-				"features": `select({
-        "//build/bazel/platforms/os:darwin": [
-            "disable_pack_relocations",
-            "-no_undefined_symbols",
-        ],
-        "//conditions:default": [],
-    })`,
-				"srcs": `["c.cpp"]`,
-			}),
-		},
+		expectedBazelTargets: expected_targets,
 	})
 }
 
 func TestCcLibrarySpacesInCopts(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library spaces in copts",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library spaces in copts",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "a",
@@ -972,23 +1022,21 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "a", attrNameToString{
-				"copts": `[
+		expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+			"copts": `[
         "-include",
         "header.h",
     ]`,
-			}),
-		},
-	})
+		}),
+	},
+	)
 }
 
 func TestCcLibraryCppFlagsGoesIntoCopts(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library cppflags usage",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library cppflags usage",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `cc_library {
     name: "a",
     srcs: ["a.cpp"],
@@ -1010,10 +1058,9 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "a", attrNameToString{
-				"copts": `["-Wall"]`,
-				"cppflags": `[
+		expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+			"copts": `["-Wall"]`,
+			"cppflags": `[
         "-fsigned-char",
         "-pedantic",
     ] + select({
@@ -1023,18 +1070,17 @@
         "//build/bazel/platforms/os:android": ["-DANDROID=1"],
         "//conditions:default": [],
     })`,
-				"srcs": `["a.cpp"]`,
-			}),
-		},
-	})
+			"srcs": `["a.cpp"]`,
+		}),
+	},
+	)
 }
 
 func TestCcLibraryExcludeLibs(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		filesystem:                         map[string]string{},
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		filesystem:                 map[string]string{},
 		blueprint: soongCcLibraryStaticPreamble + `
 cc_library {
     name: "foo_static",
@@ -1109,41 +1155,39 @@
     bazel_module: { bp2build_available: false },
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "foo_static", attrNameToString{
-				"implementation_deps": `select({
+		expectedBazelTargets: makeCcLibraryTargets("foo_static", attrNameToString{
+			"implementation_deps": `select({
         "//build/bazel/platforms/arch:arm": [],
         "//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_bp2build_cc_library_static"],
     })`,
-				"implementation_dynamic_deps": `select({
+			"implementation_dynamic_deps": `select({
         "//build/bazel/platforms/arch:arm": [],
         "//conditions:default": [":arm_shared_lib_excludes"],
     }) + select({
         "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_shared_lib"],
         "//conditions:default": [],
     })`,
-				"srcs_c": `["common.c"]`,
-				"whole_archive_deps": `select({
+			"srcs_c": `["common.c"]`,
+			"whole_archive_deps": `select({
         "//build/bazel/platforms/arch:arm": [],
         "//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_bp2build_cc_library_static"],
         "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes_bp2build_cc_library_static"],
     })`,
-			}),
-		},
-	})
+		}),
+	},
+	)
 }
 
 func TestCCLibraryNoCrtTrue(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library - nocrt: true emits attribute",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library - nocrt: true emits attribute",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		filesystem: map[string]string{
 			"impl.cpp": "",
 		},
@@ -1155,21 +1199,19 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "foo-lib", attrNameToString{
-				"link_crt": `False`,
-				"srcs":     `["impl.cpp"]`,
-			}),
-		},
-	})
+		expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+			"link_crt": `False`,
+			"srcs":     `["impl.cpp"]`,
+		}),
+	},
+	)
 }
 
 func TestCCLibraryNoCrtFalse(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library - nocrt: false - does not emit attribute",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library - nocrt: false - does not emit attribute",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		filesystem: map[string]string{
 			"impl.cpp": "",
 		},
@@ -1181,20 +1223,17 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "foo-lib", attrNameToString{
-				"srcs": `["impl.cpp"]`,
-			}),
-		},
+		expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+			"srcs": `["impl.cpp"]`,
+		}),
 	})
 }
 
 func TestCCLibraryNoCrtArchVariant(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library - nocrt in select",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library - nocrt in select",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		filesystem: map[string]string{
 			"impl.cpp": "",
 		},
@@ -1213,21 +1252,18 @@
     include_build_directory: false,
 }
 `,
-		expectedErr: fmt.Errorf("Android.bp:16:1: module \"foo-lib\": nocrt is not supported for arch variants"),
+		expectedErr: fmt.Errorf("module \"foo-lib\": nocrt is not supported for arch variants"),
 	})
 }
 
 func TestCCLibraryNoLibCrtTrue(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library - simple example",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		filesystem: map[string]string{
 			"impl.cpp": "",
 		},
 		blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "some-headers" }
 cc_library {
     name: "foo-lib",
     srcs: ["impl.cpp"],
@@ -1235,24 +1271,48 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "foo-lib", attrNameToString{
-				"srcs":       `["impl.cpp"]`,
-				"use_libcrt": `False`,
-			}),
-		}})
+		expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+			"srcs":       `["impl.cpp"]`,
+			"use_libcrt": `False`,
+		}),
+	})
+}
+
+func makeCcLibraryTargets(name string, attrs attrNameToString) []string {
+	STATIC_ONLY_ATTRS := map[string]bool{}
+	SHARED_ONLY_ATTRS := map[string]bool{
+		"link_crt":                 true,
+		"additional_linker_inputs": true,
+		"linkopts":                 true,
+		"strip":                    true,
+		"stubs_symbol_file":        true,
+		"stubs_versions":           true,
+		"inject_bssl_hash":         true,
+	}
+	sharedAttrs := attrNameToString{}
+	staticAttrs := attrNameToString{}
+	for key, val := range attrs {
+		if _, staticOnly := STATIC_ONLY_ATTRS[key]; !staticOnly {
+			sharedAttrs[key] = val
+		}
+		if _, sharedOnly := SHARED_ONLY_ATTRS[key]; !sharedOnly {
+			staticAttrs[key] = val
+		}
+	}
+	sharedTarget := makeBazelTarget("cc_library_shared", name, sharedAttrs)
+	staticTarget := makeBazelTarget("cc_library_static", name+"_bp2build_cc_library_static", staticAttrs)
+
+	return []string{staticTarget, sharedTarget}
 }
 
 func TestCCLibraryNoLibCrtFalse(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		filesystem: map[string]string{
 			"impl.cpp": "",
 		},
 		blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "some-headers" }
 cc_library {
     name: "foo-lib",
     srcs: ["impl.cpp"],
@@ -1260,19 +1320,17 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "foo-lib", attrNameToString{
-				"srcs":       `["impl.cpp"]`,
-				"use_libcrt": `True`,
-			}),
-		}})
+		expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+			"srcs":       `["impl.cpp"]`,
+			"use_libcrt": `True`,
+		}),
+	})
 }
 
 func TestCCLibraryNoLibCrtArchVariant(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		filesystem: map[string]string{
 			"impl.cpp": "",
 		},
@@ -1291,24 +1349,138 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "foo-lib", attrNameToString{
-				"srcs": `["impl.cpp"]`,
-				"use_libcrt": `select({
+		expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+			"srcs": `["impl.cpp"]`,
+			"use_libcrt": `select({
         "//build/bazel/platforms/arch:arm": False,
         "//build/bazel/platforms/arch:x86": False,
         "//conditions:default": None,
     })`,
-			}),
-		}})
+		}),
+	})
+}
+
+func TestCCLibraryNoLibCrtArchAndTargetVariant(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		filesystem: map[string]string{
+			"impl.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "foo-lib",
+    srcs: ["impl.cpp"],
+    arch: {
+        arm: {
+            no_libcrt: true,
+        },
+        x86: {
+            no_libcrt: true,
+        },
+    },
+    target: {
+        darwin: {
+            no_libcrt: true,
+        }
+    },
+    include_build_directory: false,
+}
+`,
+		expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+			"srcs": `["impl.cpp"]`,
+			"use_libcrt": `select({
+        "//build/bazel/platforms/os_arch:android_arm": False,
+        "//build/bazel/platforms/os_arch:android_x86": False,
+        "//build/bazel/platforms/os_arch:darwin_arm64": False,
+        "//build/bazel/platforms/os_arch:darwin_x86_64": False,
+        "//build/bazel/platforms/os_arch:linux_glibc_x86": False,
+        "//build/bazel/platforms/os_arch:linux_musl_x86": False,
+        "//build/bazel/platforms/os_arch:windows_x86": False,
+        "//conditions:default": None,
+    })`,
+		}),
+	})
+}
+
+func TestCCLibraryNoLibCrtArchAndTargetVariantConflict(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		filesystem: map[string]string{
+			"impl.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "foo-lib",
+    srcs: ["impl.cpp"],
+    arch: {
+        arm: {
+            no_libcrt: true,
+        },
+        // This is expected to override the value for darwin_x86_64.
+        x86_64: {
+            no_libcrt: true,
+        },
+    },
+    target: {
+        darwin: {
+            no_libcrt: false,
+        }
+    },
+    include_build_directory: false,
+}
+`,
+		expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+			"srcs": `["impl.cpp"]`,
+			"use_libcrt": `select({
+        "//build/bazel/platforms/os_arch:android_arm": False,
+        "//build/bazel/platforms/os_arch:android_x86_64": False,
+        "//build/bazel/platforms/os_arch:darwin_arm64": True,
+        "//build/bazel/platforms/os_arch:darwin_x86_64": False,
+        "//build/bazel/platforms/os_arch:linux_bionic_x86_64": False,
+        "//build/bazel/platforms/os_arch:linux_glibc_x86_64": False,
+        "//build/bazel/platforms/os_arch:linux_musl_x86_64": False,
+        "//build/bazel/platforms/os_arch:windows_x86_64": False,
+        "//conditions:default": None,
+    })`,
+		}),
+	})
 }
 
 func TestCcLibraryStrip(t *testing.T) {
+	expectedTargets := []string{}
+	expectedTargets = append(expectedTargets, makeCcLibraryTargets("all", attrNameToString{
+		"strip": `{
+        "all": True,
+    }`,
+	})...)
+	expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols", attrNameToString{
+		"strip": `{
+        "keep_symbols": True,
+    }`,
+	})...)
+	expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols_and_debug_frame", attrNameToString{
+		"strip": `{
+        "keep_symbols_and_debug_frame": True,
+    }`,
+	})...)
+	expectedTargets = append(expectedTargets, makeCcLibraryTargets("keep_symbols_list", attrNameToString{
+		"strip": `{
+        "keep_symbols_list": ["symbol"],
+    }`,
+	})...)
+	expectedTargets = append(expectedTargets, makeCcLibraryTargets("none", attrNameToString{
+		"strip": `{
+        "none": True,
+    }`,
+	})...)
+	expectedTargets = append(expectedTargets, makeCcLibraryTargets("nothing", attrNameToString{})...)
+
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library strip args",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library strip args",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "nothing",
@@ -1350,38 +1522,15 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "all", attrNameToString{
-				"strip": `{
-        "all": True,
-    }`,
-			}), makeBazelTarget("cc_library", "keep_symbols", attrNameToString{
-				"strip": `{
-        "keep_symbols": True,
-    }`,
-			}), makeBazelTarget("cc_library", "keep_symbols_and_debug_frame", attrNameToString{
-				"strip": `{
-        "keep_symbols_and_debug_frame": True,
-    }`,
-			}), makeBazelTarget("cc_library", "keep_symbols_list", attrNameToString{
-				"strip": `{
-        "keep_symbols_list": ["symbol"],
-    }`,
-			}), makeBazelTarget("cc_library", "none", attrNameToString{
-				"strip": `{
-        "none": True,
-    }`,
-			}), makeBazelTarget("cc_library", "nothing", attrNameToString{}),
-		},
+		expectedBazelTargets: expectedTargets,
 	})
 }
 
 func TestCcLibraryStripWithArch(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library strip args",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library strip args",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "multi-arch",
@@ -1407,9 +1556,8 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "multi-arch", attrNameToString{
-				"strip": `{
+		expectedBazelTargets: makeCcLibraryTargets("multi-arch", attrNameToString{
+			"strip": `{
         "keep_symbols": select({
             "//build/bazel/platforms/arch:arm64": True,
             "//conditions:default": None,
@@ -1426,17 +1574,16 @@
             "//conditions:default": [],
         }),
     }`,
-			}),
-		},
-	})
+		}),
+	},
+	)
 }
 
 func TestCcLibrary_SystemSharedLibsRootEmpty(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library system_shared_libs empty at root",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library system_shared_libs empty at root",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "root_empty",
@@ -1444,20 +1591,18 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "root_empty", attrNameToString{
-				"system_dynamic_deps": `[]`,
-			}),
-		},
-	})
+		expectedBazelTargets: makeCcLibraryTargets("root_empty", attrNameToString{
+			"system_dynamic_deps": `[]`,
+		}),
+	},
+	)
 }
 
 func TestCcLibrary_SystemSharedLibsStaticEmpty(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library system_shared_libs empty for static variant",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library system_shared_libs empty for static variant",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "static_empty",
@@ -1468,21 +1613,19 @@
 }
 `,
 		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "static_empty", attrNameToString{
-				"static": `{
-        "system_dynamic_deps": [],
-    }`,
+			makeBazelTarget("cc_library_static", "static_empty_bp2build_cc_library_static", attrNameToString{
+				"system_dynamic_deps": "[]",
 			}),
+			makeBazelTarget("cc_library_shared", "static_empty", attrNameToString{}),
 		},
 	})
 }
 
 func TestCcLibrary_SystemSharedLibsSharedEmpty(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library system_shared_libs empty for shared variant",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library system_shared_libs empty for shared variant",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "shared_empty",
@@ -1493,10 +1636,9 @@
 }
 `,
 		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "shared_empty", attrNameToString{
-				"shared": `{
-        "system_dynamic_deps": [],
-    }`,
+			makeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", attrNameToString{}),
+			makeBazelTarget("cc_library_shared", "shared_empty", attrNameToString{
+				"system_dynamic_deps": "[]",
 			}),
 		},
 	})
@@ -1504,10 +1646,9 @@
 
 func TestCcLibrary_SystemSharedLibsSharedBionicEmpty(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library system_shared_libs empty for shared, bionic variant",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library system_shared_libs empty for shared, bionic variant",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "shared_empty",
@@ -1522,10 +1663,9 @@
 }
 `,
 		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "shared_empty", attrNameToString{
-				"shared": `{
-        "system_dynamic_deps": [],
-    }`,
+			makeBazelTarget("cc_library_static", "shared_empty_bp2build_cc_library_static", attrNameToString{}),
+			makeBazelTarget("cc_library_shared", "shared_empty", attrNameToString{
+				"system_dynamic_deps": "[]",
 			}),
 		},
 	})
@@ -1537,10 +1677,9 @@
 	// only for linux_bionic, but `android` had `["libc", "libdl", "libm"].
 	// b/195791252 tracks the fix.
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library system_shared_libs empty for linux_bionic variant",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library system_shared_libs empty for linux_bionic variant",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "target_linux_bionic_empty",
@@ -1552,20 +1691,18 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "target_linux_bionic_empty", attrNameToString{
-				"system_dynamic_deps": `[]`,
-			}),
-		},
-	})
+		expectedBazelTargets: makeCcLibraryTargets("target_linux_bionic_empty", attrNameToString{
+			"system_dynamic_deps": `[]`,
+		}),
+	},
+	)
 }
 
 func TestCcLibrary_SystemSharedLibsBionicEmpty(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library system_shared_libs empty for bionic variant",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library system_shared_libs empty for bionic variant",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "target_bionic_empty",
@@ -1577,20 +1714,18 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "target_bionic_empty", attrNameToString{
-				"system_dynamic_deps": `[]`,
-			}),
-		},
-	})
+		expectedBazelTargets: makeCcLibraryTargets("target_bionic_empty", attrNameToString{
+			"system_dynamic_deps": `[]`,
+		}),
+	},
+	)
 }
 
 func TestCcLibrary_SystemSharedLibsSharedAndRoot(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library system_shared_libs set for shared and root",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		description:                "cc_library system_shared_libs set for shared and root",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
 		blueprint: soongCcLibraryPreamble + `
 cc_library {
     name: "libc",
@@ -1611,25 +1746,26 @@
 }
 `,
 		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "foo", attrNameToString{
-				"shared": `{
-        "system_dynamic_deps": [":libm"],
-    }`,
+			makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
 				"system_dynamic_deps": `[":libc"]`,
 			}),
+			makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+				"system_dynamic_deps": `[
+        ":libc",
+        ":libm",
+    ]`,
+			}),
 		},
 	})
 }
 
 func TestCcLibraryOsSelects(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library - selects for all os targets",
-		moduleTypeUnderTest:                "cc_library",
-		moduleTypeUnderTestFactory:         cc.LibraryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-		filesystem:                         map[string]string{},
+		description:                "cc_library - selects for all os targets",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		filesystem:                 map[string]string{},
 		blueprint: soongCcLibraryPreamble + `
-cc_library_headers { name: "some-headers" }
 cc_library {
     name: "foo-lib",
     srcs: ["base.cpp"],
@@ -1659,9 +1795,8 @@
     include_build_directory: false,
 }
 `,
-		expectedBazelTargets: []string{
-			makeBazelTarget("cc_library", "foo-lib", attrNameToString{
-				"srcs": `["base.cpp"] + select({
+		expectedBazelTargets: makeCcLibraryTargets("foo-lib", attrNameToString{
+			"srcs": `["base.cpp"] + select({
         "//build/bazel/platforms/os:android": [
             "linux.cpp",
             "bionic.cpp",
@@ -1683,9 +1818,36 @@
         "//build/bazel/platforms/os:windows": ["windows.cpp"],
         "//conditions:default": [],
     })`,
-			}),
-		},
-	})
+		}),
+	},
+	)
+}
+
+func TestLibcryptoHashInjection(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                "cc_library - libcrypto hash injection",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		filesystem:                 map[string]string{},
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "libcrypto",
+    target: {
+        android: {
+            inject_bssl_hash: true,
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		expectedBazelTargets: makeCcLibraryTargets("libcrypto", attrNameToString{
+			"inject_bssl_hash": `select({
+        "//build/bazel/platforms/os:android": True,
+        "//conditions:default": None,
+    })`,
+		}),
+	},
+	)
 }
 
 func TestCcLibraryCppStdWithGnuExtensions_ConvertsToFeatureAttr(t *testing.T) {
@@ -1770,10 +1932,9 @@
 
 		runCcLibraryTestCase(t, bp2buildTestCase{
 			description: fmt.Sprintf(
-				"cc_library with c_std: %s, cpp_std: %s and gnu_extensions: %s", tc.c_std, tc.cpp_std, tc.gnu_extensions),
-			moduleTypeUnderTest:                "cc_library",
-			moduleTypeUnderTestFactory:         cc.LibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+				"cc_library with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
+			moduleTypeUnderTest:        "cc_library",
+			moduleTypeUnderTestFactory: cc.LibraryFactory,
 			blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
 cc_library {
 	name: "%s_full",
@@ -1783,17 +1944,14 @@
 	include_build_directory: false,
 }
 `, name_prefix, cppStdProp, cStdProp, gnuExtensionsProp),
-			expectedBazelTargets: []string{
-				makeBazelTarget("cc_library", name_prefix+"_full", attrs),
-			},
+			expectedBazelTargets: makeCcLibraryTargets(name_prefix+"_full", attrs),
 		})
 
 		runCcLibraryStaticTestCase(t, bp2buildTestCase{
 			description: fmt.Sprintf(
-				"cc_library_static with c_std: %s, cpp_std: %s and gnu_extensions: %s", tc.c_std, tc.cpp_std, tc.gnu_extensions),
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+				"cc_library_static with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
+			moduleTypeUnderTest:        "cc_library_static",
+			moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
 			blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
 cc_library_static {
 	name: "%s_static",
@@ -1810,10 +1968,9 @@
 
 		runCcLibrarySharedTestCase(t, bp2buildTestCase{
 			description: fmt.Sprintf(
-				"cc_library_shared with c_std: %s, cpp_std: %s and gnu_extensions: %s", tc.c_std, tc.cpp_std, tc.gnu_extensions),
-			moduleTypeUnderTest:                "cc_library_shared",
-			moduleTypeUnderTestFactory:         cc.LibrarySharedFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibrarySharedBp2Build,
+				"cc_library_shared with cpp_std: %s and gnu_extensions: %s", tc.cpp_std, tc.gnu_extensions),
+			moduleTypeUnderTest:        "cc_library_shared",
+			moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
 			blueprint: soongCcLibraryPreamble + fmt.Sprintf(`
 cc_library_shared {
 	name: "%s_shared",
@@ -1829,3 +1986,461 @@
 		})
 	}
 }
+
+func TestCcLibraryProtoSimple(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.proto"],
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+				"srcs": `["foo.proto"]`,
+			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+				"deps": `[":foo_proto"]`,
+			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
+				"deps":                              `[":libprotobuf-cpp-lite"]`,
+			}), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+				"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryProtoNoCanonicalPathFromRoot(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.proto"],
+	proto: { canonical_path_from_root: false},
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+				"srcs":                `["foo.proto"]`,
+				"strip_import_prefix": `""`,
+			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+				"deps": `[":foo_proto"]`,
+			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
+				"deps":                              `[":libprotobuf-cpp-lite"]`,
+			}), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+				"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryProtoExplicitCanonicalPathFromRoot(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.proto"],
+	proto: { canonical_path_from_root: true},
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+				"srcs": `["foo.proto"]`,
+			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+				"deps": `[":foo_proto"]`,
+			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
+				"deps":                              `[":libprotobuf-cpp-lite"]`,
+			}), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+				"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryProtoFull(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.proto"],
+	proto: {
+		type: "full",
+	},
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+				"srcs": `["foo.proto"]`,
+			}), makeBazelTarget("cc_proto_library", "foo_cc_proto", attrNameToString{
+				"deps": `[":foo_proto"]`,
+			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_proto"]`,
+				"deps":                              `[":libprotobuf-cpp-full"]`,
+			}), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+				"dynamic_deps": `[":libprotobuf-cpp-full"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryProtoLite(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.proto"],
+	proto: {
+		type: "lite",
+	},
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+				"srcs": `["foo.proto"]`,
+			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+				"deps": `[":foo_proto"]`,
+			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_proto_lite"]`,
+				"deps":                              `[":libprotobuf-cpp-lite"]`,
+			}), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+				"dynamic_deps": `[":libprotobuf-cpp-lite"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryProtoExportHeaders(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.proto"],
+	proto: {
+		export_proto_headers: true,
+	},
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+				"srcs": `["foo.proto"]`,
+			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+				"deps": `[":foo_proto"]`,
+			}), makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+				"deps":               `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":foo_cc_proto_lite"]`,
+			}), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":foo_cc_proto_lite"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryProtoFilegroups(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble +
+			simpleModuleDoNotConvertBp2build("filegroup", "a_fg_proto") +
+			simpleModuleDoNotConvertBp2build("filegroup", "b_protos") +
+			simpleModuleDoNotConvertBp2build("filegroup", "c-proto-srcs") +
+			simpleModuleDoNotConvertBp2build("filegroup", "proto-srcs-d") + `
+cc_library {
+	name: "a",
+	srcs: [":a_fg_proto"],
+	proto: {
+		export_proto_headers: true,
+	},
+	include_build_directory: false,
+}
+
+cc_library {
+	name: "b",
+	srcs: [":b_protos"],
+	proto: {
+		export_proto_headers: true,
+	},
+	include_build_directory: false,
+}
+
+cc_library {
+	name: "c",
+	srcs: [":c-proto-srcs"],
+	proto: {
+		export_proto_headers: true,
+	},
+	include_build_directory: false,
+}
+
+cc_library {
+	name: "d",
+	srcs: [":proto-srcs-d"],
+	proto: {
+		export_proto_headers: true,
+	},
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("proto_library", "a_proto", attrNameToString{
+				"srcs": `[":a_fg_proto"]`,
+			}), makeBazelTarget("cc_lite_proto_library", "a_cc_proto_lite", attrNameToString{
+				"deps": `[":a_proto"]`,
+			}), makeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", attrNameToString{
+				"deps":               `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":a_cc_proto_lite"]`,
+				"srcs":               `[":a_fg_proto_cpp_srcs"]`,
+				"srcs_as":            `[":a_fg_proto_as_srcs"]`,
+				"srcs_c":             `[":a_fg_proto_c_srcs"]`,
+			}), makeBazelTarget("cc_library_shared", "a", attrNameToString{
+				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":a_cc_proto_lite"]`,
+				"srcs":               `[":a_fg_proto_cpp_srcs"]`,
+				"srcs_as":            `[":a_fg_proto_as_srcs"]`,
+				"srcs_c":             `[":a_fg_proto_c_srcs"]`,
+			}), makeBazelTarget("proto_library", "b_proto", attrNameToString{
+				"srcs": `[":b_protos"]`,
+			}), makeBazelTarget("cc_lite_proto_library", "b_cc_proto_lite", attrNameToString{
+				"deps": `[":b_proto"]`,
+			}), makeBazelTarget("cc_library_static", "b_bp2build_cc_library_static", attrNameToString{
+				"deps":               `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":b_cc_proto_lite"]`,
+				"srcs":               `[":b_protos_cpp_srcs"]`,
+				"srcs_as":            `[":b_protos_as_srcs"]`,
+				"srcs_c":             `[":b_protos_c_srcs"]`,
+			}), makeBazelTarget("cc_library_shared", "b", attrNameToString{
+				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":b_cc_proto_lite"]`,
+				"srcs":               `[":b_protos_cpp_srcs"]`,
+				"srcs_as":            `[":b_protos_as_srcs"]`,
+				"srcs_c":             `[":b_protos_c_srcs"]`,
+			}), makeBazelTarget("proto_library", "c_proto", attrNameToString{
+				"srcs": `[":c-proto-srcs"]`,
+			}), makeBazelTarget("cc_lite_proto_library", "c_cc_proto_lite", attrNameToString{
+				"deps": `[":c_proto"]`,
+			}), makeBazelTarget("cc_library_static", "c_bp2build_cc_library_static", attrNameToString{
+				"deps":               `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":c_cc_proto_lite"]`,
+				"srcs":               `[":c-proto-srcs_cpp_srcs"]`,
+				"srcs_as":            `[":c-proto-srcs_as_srcs"]`,
+				"srcs_c":             `[":c-proto-srcs_c_srcs"]`,
+			}), makeBazelTarget("cc_library_shared", "c", attrNameToString{
+				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":c_cc_proto_lite"]`,
+				"srcs":               `[":c-proto-srcs_cpp_srcs"]`,
+				"srcs_as":            `[":c-proto-srcs_as_srcs"]`,
+				"srcs_c":             `[":c-proto-srcs_c_srcs"]`,
+			}), makeBazelTarget("proto_library", "d_proto", attrNameToString{
+				"srcs": `[":proto-srcs-d"]`,
+			}), makeBazelTarget("cc_lite_proto_library", "d_cc_proto_lite", attrNameToString{
+				"deps": `[":d_proto"]`,
+			}), makeBazelTarget("cc_library_static", "d_bp2build_cc_library_static", attrNameToString{
+				"deps":               `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":d_cc_proto_lite"]`,
+				"srcs":               `[":proto-srcs-d_cpp_srcs"]`,
+				"srcs_as":            `[":proto-srcs-d_as_srcs"]`,
+				"srcs_c":             `[":proto-srcs-d_c_srcs"]`,
+			}), makeBazelTarget("cc_library_shared", "d", attrNameToString{
+				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":d_cc_proto_lite"]`,
+				"srcs":               `[":proto-srcs-d_cpp_srcs"]`,
+				"srcs_as":            `[":proto-srcs-d_as_srcs"]`,
+				"srcs_c":             `[":proto-srcs-d_c_srcs"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryDisabledArchAndTarget(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.cpp"],
+	target: {
+		darwin: {
+			enabled: false,
+		},
+		windows: {
+			enabled: false,
+		},
+		linux_glibc_x86: {
+			enabled: false,
+		},
+	},
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: makeCcLibraryTargets("foo", attrNameToString{
+			"srcs": `["foo.cpp"]`,
+			"target_compatible_with": `select({
+        "//build/bazel/platforms/os_arch:darwin_arm64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:darwin_x86_64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:linux_glibc_x86": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:windows_x86": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:windows_x86_64": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+		}),
+	})
+}
+
+func TestCcLibraryDisabledArchAndTargetWithDefault(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.cpp"],
+  enabled: false,
+	target: {
+		darwin: {
+			enabled: true,
+		},
+		windows: {
+			enabled: false,
+		},
+		linux_glibc_x86: {
+			enabled: false,
+		},
+	},
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: makeCcLibraryTargets("foo", attrNameToString{
+			"srcs": `["foo.cpp"]`,
+			"target_compatible_with": `select({
+        "//build/bazel/platforms/os_arch:darwin_arm64": [],
+        "//build/bazel/platforms/os_arch:darwin_x86_64": [],
+        "//conditions:default": ["@platforms//:incompatible"],
+    })`,
+		}),
+	})
+}
+
+func TestCcLibrarySharedDisabled(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.cpp"],
+	enabled: false,
+	shared: {
+		enabled: true,
+	},
+	target: {
+		android: {
+			shared: {
+				enabled: false,
+			},
+		}
+  },
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+			"srcs":                   `["foo.cpp"]`,
+			"target_compatible_with": `["@platforms//:incompatible"]`,
+		}), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+			"srcs": `["foo.cpp"]`,
+			"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+		}),
+		},
+	})
+}
+
+func TestCcLibraryStaticDisabledForSomeArch(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	srcs: ["foo.cpp"],
+	shared: {
+		enabled: false
+	},
+	target: {
+		darwin: {
+			enabled: true,
+		},
+		windows: {
+			enabled: false,
+		},
+		linux_glibc_x86: {
+			shared: {
+				enabled: true,
+			},
+		},
+	},
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{makeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", attrNameToString{
+			"srcs": `["foo.cpp"]`,
+			"target_compatible_with": `select({
+        "//build/bazel/platforms/os:windows": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+		}), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+			"srcs": `["foo.cpp"]`,
+			"target_compatible_with": `select({
+        "//build/bazel/platforms/os_arch:darwin_arm64": [],
+        "//build/bazel/platforms/os_arch:darwin_x86_64": [],
+        "//build/bazel/platforms/os_arch:linux_glibc_x86": [],
+        "//conditions:default": ["@platforms//:incompatible"],
+    })`,
+		}),
+		}})
+}
+
+func TestCcLibraryStubs(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                "cc_library stubs",
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		dir:                        "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": `
+cc_library {
+    name: "a",
+    stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+    bazel_module: { bp2build_available: true },
+    include_build_directory: false,
+}
+`,
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: makeCcLibraryTargets("a", attrNameToString{
+			"stubs_symbol_file": `"a.map.txt"`,
+			"stubs_versions": `[
+        "28",
+        "29",
+        "current",
+    ]`,
+		}),
+	},
+	)
+}
+
+func TestCcLibraryEscapeLdflags(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:        "cc_library",
+		moduleTypeUnderTestFactory: cc.LibraryFactory,
+		blueprint: soongCcProtoPreamble + `cc_library {
+	name: "foo",
+	ldflags: ["-Wl,--rpath,${ORIGIN}"],
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: makeCcLibraryTargets("foo", attrNameToString{
+			"linkopts": `["-Wl,--rpath,$${ORIGIN}"]`,
+		}),
+	})
+}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index 76fdab2..e4cfa35 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -26,17 +26,6 @@
 	soongCcLibraryHeadersPreamble = `
 cc_defaults {
     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: "",
 }`
 )
 
@@ -68,7 +57,6 @@
 
 func registerCcLibraryHeadersModuleTypes(ctx android.RegistrationContext) {
 	cc.RegisterCCBuildComponents(ctx)
-	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
 }
 
 func runCcLibraryHeadersTestCase(t *testing.T, tc bp2buildTestCase) {
@@ -78,10 +66,9 @@
 
 func TestCcLibraryHeadersSimple(t *testing.T) {
 	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
-		description:                        "cc_library_headers test",
-		moduleTypeUnderTest:                "cc_library_headers",
-		moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+		description:                "cc_library_headers test",
+		moduleTypeUnderTest:        "cc_library_headers",
+		moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
 		filesystem: map[string]string{
 			"lib-1/lib1a.h":                        "",
 			"lib-1/lib1b.h":                        "",
@@ -150,11 +137,10 @@
 
 func TestCcLibraryHeadersOsSpecificHeader(t *testing.T) {
 	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
-		description:                        "cc_library_headers test with os-specific header_libs props",
-		moduleTypeUnderTest:                "cc_library_headers",
-		moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
-		filesystem:                         map[string]string{},
+		description:                "cc_library_headers test with os-specific header_libs props",
+		moduleTypeUnderTest:        "cc_library_headers",
+		moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+		filesystem:                 map[string]string{},
 		blueprint: soongCcLibraryPreamble + `
 cc_library_headers {
     name: "android-lib",
@@ -209,11 +195,10 @@
 
 func TestCcLibraryHeadersOsSpecficHeaderLibsExportHeaderLibHeaders(t *testing.T) {
 	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
-		description:                        "cc_library_headers test with os-specific header_libs and export_header_lib_headers props",
-		moduleTypeUnderTest:                "cc_library_headers",
-		moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
-		filesystem:                         map[string]string{},
+		description:                "cc_library_headers test with os-specific header_libs and export_header_lib_headers props",
+		moduleTypeUnderTest:        "cc_library_headers",
+		moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+		filesystem:                 map[string]string{},
 		blueprint: soongCcLibraryPreamble + `
 cc_library_headers {
     name: "android-lib",
@@ -250,11 +235,10 @@
 
 func TestCcLibraryHeadersArchAndTargetExportSystemIncludes(t *testing.T) {
 	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
-		description:                        "cc_library_headers test with arch-specific and target-specific export_system_include_dirs props",
-		moduleTypeUnderTest:                "cc_library_headers",
-		moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
-		filesystem:                         map[string]string{},
+		description:                "cc_library_headers test with arch-specific and target-specific export_system_include_dirs props",
+		moduleTypeUnderTest:        "cc_library_headers",
+		moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+		filesystem:                 map[string]string{},
 		blueprint: soongCcLibraryPreamble + `cc_library_headers {
     name: "foo_headers",
     export_system_include_dirs: [
@@ -310,10 +294,9 @@
 
 func TestCcLibraryHeadersNoCrtIgnored(t *testing.T) {
 	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
-		description:                        "cc_library_headers test",
-		moduleTypeUnderTest:                "cc_library_headers",
-		moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+		description:                "cc_library_headers test",
+		moduleTypeUnderTest:        "cc_library_headers",
+		moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
 		filesystem: map[string]string{
 			"lib-1/lib1a.h":                        "",
 			"lib-1/lib1b.h":                        "",
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 4ec95c3..78192fe 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -30,16 +30,15 @@
 
 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)
+	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
 }
 
 func runCcLibrarySharedTestCase(t *testing.T, tc bp2buildTestCase) {
 	t.Helper()
 	(&tc).moduleTypeUnderTest = "cc_library_shared"
 	(&tc).moduleTypeUnderTestFactory = cc.LibrarySharedFactory
-	(&tc).moduleTypeUnderTestBp2BuildMutator = cc.CcLibrarySharedBp2Build
 	runBp2BuildTestCase(t, registerCcLibrarySharedModuleTypes, tc)
 }
 
@@ -422,6 +421,74 @@
     include_build_directory: false,
 }
 `,
-		expectedErr: fmt.Errorf("Android.bp:16:1: module \"foo_shared\": nocrt is not supported for arch variants"),
+		expectedErr: fmt.Errorf("module \"foo_shared\": nocrt is not supported for arch variants"),
 	})
 }
+
+func TestCcLibrarySharedProto(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		blueprint: soongCcProtoPreamble + `cc_library_shared {
+	name: "foo",
+	srcs: ["foo.proto"],
+	proto: {
+		export_proto_headers: true,
+	},
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+				"srcs": `["foo.proto"]`,
+			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+				"deps": `[":foo_proto"]`,
+			}), makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+				"dynamic_deps":       `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":foo_cc_proto_lite"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedUseVersionLib(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		blueprint: soongCcProtoPreamble + `cc_library_shared {
+        name: "foo",
+        use_version_lib: true,
+        include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("cc_library_shared", "foo", attrNameToString{
+				"use_version_lib": "True",
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedStubs(t *testing.T) {
+	runCcLibrarySharedTestCase(t, bp2buildTestCase{
+		description:                "cc_library_shared stubs",
+		moduleTypeUnderTest:        "cc_library_shared",
+		moduleTypeUnderTestFactory: cc.LibrarySharedFactory,
+		dir:                        "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": `
+cc_library_shared {
+	name: "a",
+	stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: true },
+	include_build_directory: false,
+}
+`,
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{makeBazelTarget("cc_library_shared", "a", attrNameToString{
+			"stubs_symbol_file": `"a.map.txt"`,
+			"stubs_versions": `[
+        "28",
+        "29",
+        "current",
+    ]`,
+		}),
+		},
+	},
+	)
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 2f760d2..205bf4d 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -27,17 +27,6 @@
 	soongCcLibraryStaticPreamble = `
 cc_defaults {
     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: "",
 }`
 )
 
@@ -65,12 +54,10 @@
 			t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
 		}
 	}
-
 }
 
 func registerCcLibraryStaticModuleTypes(ctx android.RegistrationContext) {
 	cc.RegisterCCBuildComponents(ctx)
-	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
 	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
 	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
 	// Required for system_shared_libs dependencies.
@@ -82,7 +69,6 @@
 
 	(&tc).moduleTypeUnderTest = "cc_library_static"
 	(&tc).moduleTypeUnderTestFactory = cc.LibraryStaticFactory
-	(&tc).moduleTypeUnderTestBp2BuildMutator = cc.CcLibraryStaticBp2Build
 	runBp2BuildTestCase(t, registerCcLibraryStaticModuleTypes, tc)
 }
 
@@ -954,11 +940,13 @@
 genrule {
     name: "generated_hdr",
     cmd: "nothing to see here",
+    bazel_module: { bp2build_available: false },
 }
 
 genrule {
     name: "export_generated_hdr",
     cmd: "nothing to see here",
+    bazel_module: { bp2build_available: false },
 }
 
 cc_library_static {
@@ -970,7 +958,9 @@
 }`,
 		expectedBazelTargets: []string{
 			makeBazelTarget("cc_library_static", "foo_static", attrNameToString{
-				"hdrs": `[":export_generated_hdr"]`,
+				"export_includes": `["."]`,
+				"local_includes":  `["."]`,
+				"hdrs":            `[":export_generated_hdr"]`,
 				"srcs": `[
         "cpp_src.cpp",
         ":generated_hdr",
@@ -996,66 +986,38 @@
 			"for-x86.cpp":            "",
 			"not-for-x86.cpp":        "",
 			"not-for-everything.cpp": "",
-			"dep/Android.bp": `
-genrule {
-  name: "generated_src_other_pkg",
-  cmd: "nothing to see here",
-}
-
-genrule {
-  name: "generated_hdr_other_pkg",
-  cmd: "nothing to see here",
-}
-
-genrule {
-  name: "generated_hdr_other_pkg_x86",
-  cmd: "nothing to see here",
-}
-
-genrule {
-  name: "generated_hdr_other_pkg_android",
-  cmd: "nothing to see here",
-}`,
+			"dep/Android.bp": simpleModuleDoNotConvertBp2build("genrule", "generated_src_other_pkg") +
+				simpleModuleDoNotConvertBp2build("genrule", "generated_hdr_other_pkg") +
+				simpleModuleDoNotConvertBp2build("genrule", "generated_src_other_pkg_x86") +
+				simpleModuleDoNotConvertBp2build("genrule", "generated_hdr_other_pkg_x86") +
+				simpleModuleDoNotConvertBp2build("genrule", "generated_hdr_other_pkg_android"),
 		},
-		blueprint: soongCcLibraryStaticPreamble + `
-genrule {
-    name: "generated_src",
-    cmd: "nothing to see here",
-}
-
-genrule {
-    name: "generated_src_not_x86",
-    cmd: "nothing to see here",
-}
-
-genrule {
-    name: "generated_src_android",
-    cmd: "nothing to see here",
-}
-
-genrule {
-    name: "generated_hdr",
-    cmd: "nothing to see here",
-}
-
+		blueprint: soongCcLibraryStaticPreamble +
+			simpleModuleDoNotConvertBp2build("genrule", "generated_src") +
+			simpleModuleDoNotConvertBp2build("genrule", "generated_src_not_x86") +
+			simpleModuleDoNotConvertBp2build("genrule", "generated_src_android") +
+			simpleModuleDoNotConvertBp2build("genrule", "generated_hdr") + `
 cc_library_static {
     name: "foo_static",
     srcs: ["common.cpp", "not-for-*.cpp"],
     exclude_srcs: ["not-for-everything.cpp"],
     generated_sources: ["generated_src", "generated_src_other_pkg", "generated_src_not_x86"],
     generated_headers: ["generated_hdr", "generated_hdr_other_pkg"],
+    export_generated_headers: ["generated_hdr_other_pkg"],
     arch: {
         x86: {
           srcs: ["for-x86.cpp"],
           exclude_srcs: ["not-for-x86.cpp"],
           generated_headers: ["generated_hdr_other_pkg_x86"],
           exclude_generated_sources: ["generated_src_not_x86"],
+    export_generated_headers: ["generated_hdr_other_pkg_x86"],
         },
     },
     target: {
         android: {
             generated_sources: ["generated_src_android"],
             generated_headers: ["generated_hdr_other_pkg_android"],
+    export_generated_headers: ["generated_hdr_other_pkg_android"],
         },
     },
 
@@ -1069,23 +1031,25 @@
         ":generated_src",
         "//dep:generated_src_other_pkg",
         ":generated_hdr",
-        "//dep:generated_hdr_other_pkg",
     ] + select({
-        "//build/bazel/platforms/arch:x86": [
-            "for-x86.cpp",
-            "//dep:generated_hdr_other_pkg_x86",
-        ],
+        "//build/bazel/platforms/arch:x86": ["for-x86.cpp"],
         "//conditions:default": [
             "not-for-x86.cpp",
             ":generated_src_not_x86",
         ],
     }) + select({
-        "//build/bazel/platforms/os:android": [
-            ":generated_src_android",
-            "//dep:generated_hdr_other_pkg_android",
-        ],
+        "//build/bazel/platforms/os:android": [":generated_src_android"],
         "//conditions:default": [],
     })`,
+				"hdrs": `["//dep:generated_hdr_other_pkg"] + select({
+        "//build/bazel/platforms/arch:x86": ["//dep:generated_hdr_other_pkg_x86"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:android": ["//dep:generated_hdr_other_pkg_android"],
+        "//conditions:default": [],
+    })`,
+				"local_includes":           `["."]`,
+				"export_absolute_includes": `["dep"]`,
 			}),
 		},
 	})
@@ -1366,9 +1330,8 @@
 func TestStaticLibrary_SystemSharedLibsBionic(t *testing.T) {
 	runCcLibraryStaticTestCase(t, bp2buildTestCase{
 		description: "cc_library_static system_shared_libs set for bionic variant",
-		blueprint: soongCcLibraryStaticPreamble + `
-cc_library{name: "libc"}
-
+		blueprint: soongCcLibraryStaticPreamble +
+			simpleModuleDoNotConvertBp2build("cc_library", "libc") + `
 cc_library_static {
     name: "target_bionic",
     target: {
@@ -1394,10 +1357,9 @@
 func TestStaticLibrary_SystemSharedLibsLinuxRootAndLinuxBionic(t *testing.T) {
 	runCcLibraryStaticTestCase(t, bp2buildTestCase{
 		description: "cc_library_static system_shared_libs set for root and linux_bionic variant",
-		blueprint: soongCcLibraryStaticPreamble + `
-cc_library{name: "libc"}
-cc_library{name: "libm"}
-
+		blueprint: soongCcLibraryStaticPreamble +
+			simpleModuleDoNotConvertBp2build("cc_library", "libc") +
+			simpleModuleDoNotConvertBp2build("cc_library", "libm") + `
 cc_library_static {
     name: "target_linux_bionic",
     system_shared_libs: ["libc"],
@@ -1419,3 +1381,107 @@
 		},
 	})
 }
+
+func TestCcLibrarystatic_SystemSharedLibUsedAsDep(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description: "cc_library_static system_shared_lib empty for linux_bionic variant",
+		blueprint: soongCcLibraryStaticPreamble +
+			simpleModuleDoNotConvertBp2build("cc_library", "libc") + `
+cc_library_static {
+    name: "used_in_bionic_oses",
+    target: {
+        android: {
+            shared_libs: ["libc"],
+        },
+        linux_bionic: {
+            shared_libs: ["libc"],
+        },
+    },
+    include_build_directory: false,
+}
+
+cc_library_static {
+    name: "all",
+    shared_libs: ["libc"],
+    include_build_directory: false,
+}
+
+cc_library_static {
+    name: "keep_for_empty_system_shared_libs",
+    shared_libs: ["libc"],
+		system_shared_libs: [],
+    include_build_directory: false,
+}
+`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("cc_library_static", "all", attrNameToString{
+				"implementation_dynamic_deps": `select({
+        "//build/bazel/platforms/os:android": [],
+        "//build/bazel/platforms/os:linux_bionic": [],
+        "//conditions:default": [":libc"],
+    })`,
+			}),
+			makeBazelTarget("cc_library_static", "keep_for_empty_system_shared_libs", attrNameToString{
+				"implementation_dynamic_deps": `[":libc"]`,
+				"system_dynamic_deps":         `[]`,
+			}),
+			makeBazelTarget("cc_library_static", "used_in_bionic_oses", attrNameToString{}),
+		},
+	})
+}
+
+func TestCcLibraryStaticProto(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		blueprint: soongCcProtoPreamble + `cc_library_static {
+	name: "foo",
+	srcs: ["foo.proto"],
+	proto: {
+		export_proto_headers: true,
+	},
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("proto_library", "foo_proto", attrNameToString{
+				"srcs": `["foo.proto"]`,
+			}), makeBazelTarget("cc_lite_proto_library", "foo_cc_proto_lite", attrNameToString{
+				"deps": `[":foo_proto"]`,
+			}), makeBazelTarget("cc_library_static", "foo", attrNameToString{
+				"deps":               `[":libprotobuf-cpp-lite"]`,
+				"whole_archive_deps": `[":foo_cc_proto_lite"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryStaticUseVersionLib(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		blueprint: soongCcProtoPreamble + `cc_library_static {
+	name: "foo",
+	use_version_lib: true,
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("cc_library_static", "foo", attrNameToString{
+				"use_version_lib": "True",
+			}),
+		},
+	})
+}
+
+func TestCcLibraryStaticStdInFlags(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		blueprint: soongCcProtoPreamble + `cc_library_static {
+	name: "foo",
+	cflags: ["-std=candcpp"],
+	conlyflags: ["-std=conly"],
+	cppflags: ["-std=cpp"],
+	include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("cc_library_static", "foo", attrNameToString{
+				"conlyflags": `["-std=conly"]`,
+				"cppflags":   `["-std=cpp"]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 5ab9129..0a6c317 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -30,7 +30,6 @@
 	t.Helper()
 	(&tc).moduleTypeUnderTest = "cc_object"
 	(&tc).moduleTypeUnderTestFactory = cc.ObjectFactory
-	(&tc).moduleTypeUnderTestBp2BuildMutator = cc.ObjectBp2Build
 	runBp2BuildTestCase(t, registerCcObjectModuleTypes, tc)
 }
 
diff --git a/bp2build/cc_prebuilt_library_shared_test.go b/bp2build/cc_prebuilt_library_shared_test.go
index bac3908..ef2fddc 100644
--- a/bp2build/cc_prebuilt_library_shared_test.go
+++ b/bp2build/cc_prebuilt_library_shared_test.go
@@ -9,10 +9,9 @@
 func TestSharedPrebuiltLibrary(t *testing.T) {
 	runBp2BuildTestCaseSimple(t,
 		bp2buildTestCase{
-			description:                        "prebuilt library shared simple",
-			moduleTypeUnderTest:                "cc_prebuilt_library_shared",
-			moduleTypeUnderTestFactory:         cc.PrebuiltSharedLibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.PrebuiltLibrarySharedBp2Build,
+			description:                "prebuilt library shared simple",
+			moduleTypeUnderTest:        "cc_prebuilt_library_shared",
+			moduleTypeUnderTestFactory: cc.PrebuiltSharedLibraryFactory,
 			filesystem: map[string]string{
 				"libf.so": "",
 			},
@@ -33,10 +32,9 @@
 func TestSharedPrebuiltLibraryWithArchVariance(t *testing.T) {
 	runBp2BuildTestCaseSimple(t,
 		bp2buildTestCase{
-			description:                        "prebuilt library shared with arch variance",
-			moduleTypeUnderTest:                "cc_prebuilt_library_shared",
-			moduleTypeUnderTestFactory:         cc.PrebuiltSharedLibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.PrebuiltLibrarySharedBp2Build,
+			description:                "prebuilt library shared with arch variance",
+			moduleTypeUnderTest:        "cc_prebuilt_library_shared",
+			moduleTypeUnderTestFactory: cc.PrebuiltSharedLibraryFactory,
 			filesystem: map[string]string{
 				"libf.so": "",
 				"libg.so": "",
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index c953259..d37a523 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -1,10 +1,12 @@
 package bp2build
 
 import (
-	"android/soong/android"
-	"android/soong/bazel"
 	"fmt"
 	"reflect"
+
+	"android/soong/android"
+	"android/soong/bazel"
+	"android/soong/starlark_fmt"
 )
 
 // Configurability support for bp2build.
@@ -89,13 +91,15 @@
 		}
 		archSelects := map[string]reflect.Value{}
 		defaultVal := configToLabels[bazel.ConditionsDefaultConfigKey]
+		// Skip empty list values unless ether EmitEmptyList is true, or these values differ from the default.
+		emitEmptyList := list.EmitEmptyList || len(defaultVal.Includes) > 0
 		for config, labels := range configToLabels {
 			// Omit any entries in the map which match the default value, for brevity.
 			if config != bazel.ConditionsDefaultConfigKey && labels.Equals(defaultVal) {
 				continue
 			}
 			selectKey := axis.SelectKey(config)
-			if use, value := labelListSelectValue(selectKey, labels, list.EmitEmptyList); use {
+			if use, value := labelListSelectValue(selectKey, labels, emitEmptyList); use {
 				archSelects[selectKey] = value
 			}
 		}
@@ -144,9 +148,15 @@
 			shouldPrintDefault = true
 		}
 	case bazel.LabelAttribute:
+		if err := list.Collapse(); err != nil {
+			return "", err
+		}
 		value, configurableAttrs = getLabelValue(list)
 		defaultSelectValue = &bazelNone
 	case bazel.BoolAttribute:
+		if err := list.Collapse(); err != nil {
+			return "", err
+		}
 		value, configurableAttrs = getBoolValue(list)
 		defaultSelectValue = &bazelNone
 	default:
@@ -204,11 +214,12 @@
 			continue
 		}
 		value := selectMap[selectKey]
-		if isZero(value) && !emitZeroValues {
-			// Ignore zero values to not generate empty lists.
+		if isZero(value) && !emitZeroValues && isZero(selectMap[bazel.ConditionsDefaultSelectKey]) {
+			// Ignore zero values to not generate empty lists. However, always note zero values if
+			// the default value is non-zero.
 			continue
 		}
-		s, err := prettyPrintSelectEntry(value, selectKey, indent, emitZeroValues)
+		s, err := prettyPrintSelectEntry(value, selectKey, indent, true)
 		if err != nil {
 			return "", err
 		}
@@ -240,10 +251,10 @@
 	} else if defaultValue != nil {
 		// Print an explicit empty list (the default value) even if the value is
 		// empty, to avoid errors about not finding a configuration that matches.
-		ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
+		ret += fmt.Sprintf("%s\"%s\": %s,\n", starlark_fmt.Indention(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
 	}
 
-	ret += makeIndent(indent)
+	ret += starlark_fmt.Indention(indent)
 	ret += "})"
 
 	return ret, nil
@@ -252,7 +263,7 @@
 // prettyPrintSelectEntry converts a reflect.Value into an entry in a select map
 // with a provided key.
 func prettyPrintSelectEntry(value reflect.Value, key string, indent int, emitZeroValues bool) (string, error) {
-	s := makeIndent(indent + 1)
+	s := starlark_fmt.Indention(indent + 1)
 	v, err := prettyPrint(value, indent+1, emitZeroValues)
 	if err != nil {
 		return "", err
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 81a4b26..96c12d3 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -1,6 +1,7 @@
 package bp2build
 
 import (
+	"encoding/json"
 	"fmt"
 	"reflect"
 	"strings"
@@ -27,6 +28,13 @@
 
 	files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
 
+	apiLevelsContent, err := json.Marshal(android.GetApiLevelsMap(cfg))
+	if err != nil {
+		panic(err)
+	}
+	files = append(files, newFile("api_levels", GeneratedBuildFileName, `exports_files(["api_levels.json"])`))
+	files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
+
 	return files
 }
 
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 3e6d9e6..629ca9b 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -102,6 +102,14 @@
 			dir:      "product_config",
 			basename: "soong_config_variables.bzl",
 		},
+		{
+			dir:      "api_levels",
+			basename: GeneratedBuildFileName,
+		},
+		{
+			dir:      "api_levels",
+			basename: "api_levels.json",
+		},
 	}
 
 	if len(files) != len(expectedFilePaths) {
diff --git a/bp2build/filegroup_conversion_test.go b/bp2build/filegroup_conversion_test.go
index 9f4add2..b43cf53 100644
--- a/bp2build/filegroup_conversion_test.go
+++ b/bp2build/filegroup_conversion_test.go
@@ -25,7 +25,6 @@
 	t.Helper()
 	(&tc).moduleTypeUnderTest = "filegroup"
 	(&tc).moduleTypeUnderTestFactory = android.FileGroupFactory
-	(&tc).moduleTypeUnderTestBp2BuildMutator = android.FilegroupBp2Build
 	runBp2BuildTestCase(t, registerFilegroupModuleTypes, tc)
 }
 
diff --git a/bp2build/genrule_conversion_test.go b/bp2build/genrule_conversion_test.go
index 5976666..9244b99 100644
--- a/bp2build/genrule_conversion_test.go
+++ b/bp2build/genrule_conversion_test.go
@@ -16,7 +16,10 @@
 
 import (
 	"android/soong/android"
+	"android/soong/cc"
 	"android/soong/genrule"
+	"android/soong/java"
+	"fmt"
 	"testing"
 )
 
@@ -28,62 +31,126 @@
 	t.Helper()
 	(&tc).moduleTypeUnderTest = "genrule"
 	(&tc).moduleTypeUnderTestFactory = genrule.GenRuleFactory
-	(&tc).moduleTypeUnderTestBp2BuildMutator = genrule.GenruleBp2Build
 	runBp2BuildTestCase(t, registerGenruleModuleTypes, tc)
 }
 
-func TestGenruleBp2Build(t *testing.T) {
-	otherGenruleBp := map[string]string{
-		"other/Android.bp": `genrule {
+func otherGenruleBp(genruleTarget string) map[string]string {
+	return map[string]string{
+		"other/Android.bp": fmt.Sprintf(`%s {
     name: "foo.tool",
     out: ["foo_tool.out"],
     srcs: ["foo_tool.in"],
     cmd: "cp $(in) $(out)",
 }
-genrule {
+%s {
     name: "other.tool",
     out: ["other_tool.out"],
     srcs: ["other_tool.in"],
     cmd: "cp $(in) $(out)",
-}`,
+}`, genruleTarget, genruleTarget),
+	}
+}
+
+func TestGenruleCliVariableReplacement(t *testing.T) {
+	testCases := []struct {
+		moduleType string
+		factory    android.ModuleFactory
+		genDir     string
+	}{
+		{
+			moduleType: "genrule",
+			factory:    genrule.GenRuleFactory,
+			genDir:     "$(GENDIR)",
+		},
+		{
+			moduleType: "cc_genrule",
+			factory:    cc.GenRuleFactory,
+			genDir:     "$(RULEDIR)",
+		},
+		{
+			moduleType: "java_genrule",
+			factory:    java.GenRuleFactory,
+			genDir:     "$(RULEDIR)",
+		},
+		{
+			moduleType: "java_genrule_host",
+			factory:    java.GenRuleFactoryHost,
+			genDir:     "$(RULEDIR)",
+		},
 	}
 
-	testCases := []bp2buildTestCase{
-		{
-			description: "genrule with command line variable replacements",
-			blueprint: `genrule {
+	bp := `%s {
     name: "foo.tool",
     out: ["foo_tool.out"],
     srcs: ["foo_tool.in"],
     cmd: "cp $(in) $(out)",
-    bazel_module: { bp2build_available: true },
+    bazel_module: { bp2build_available: false },
 }
 
-genrule {
+%s {
     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{
-				makeBazelTarget("genrule", "foo", attrNameToString{
-					"cmd":   `"$(location :foo.tool) --genDir=$(GENDIR) arg $(SRCS) $(OUTS)"`,
-					"outs":  `["foo.out"]`,
-					"srcs":  `["foo.in"]`,
-					"tools": `[":foo.tool"]`,
-				}),
-				makeBazelTarget("genrule", "foo.tool", attrNameToString{
-					"cmd":  `"cp $(SRCS) $(OUTS)"`,
-					"outs": `["foo_tool.out"]`,
-					"srcs": `["foo_tool.in"]`,
-				}),
-			},
+}`
+
+	for _, tc := range testCases {
+		moduleAttrs := attrNameToString{
+			"cmd":   fmt.Sprintf(`"$(location :foo.tool) --genDir=%s arg $(SRCS) $(OUTS)"`, tc.genDir),
+			"outs":  `["foo.out"]`,
+			"srcs":  `["foo.in"]`,
+			"tools": `[":foo.tool"]`,
+		}
+
+		if tc.moduleType == "java_genrule_host" {
+			moduleAttrs["target_compatible_with"] = `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`
+		}
+
+		expectedBazelTargets := []string{
+			makeBazelTarget("genrule", "foo", moduleAttrs),
+		}
+
+		t.Run(tc.moduleType, func(t *testing.T) {
+			runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+				bp2buildTestCase{
+					moduleTypeUnderTest:        tc.moduleType,
+					moduleTypeUnderTestFactory: tc.factory,
+					blueprint:                  fmt.Sprintf(bp, tc.moduleType, tc.moduleType),
+					expectedBazelTargets:       expectedBazelTargets,
+				})
+		})
+	}
+}
+
+func TestGenruleLocationsLabel(t *testing.T) {
+	testCases := []struct {
+		moduleType string
+		factory    android.ModuleFactory
+	}{
+		{
+			moduleType: "genrule",
+			factory:    genrule.GenRuleFactory,
 		},
 		{
-			description: "genrule using $(locations :label)",
-			blueprint: `genrule {
+			moduleType: "cc_genrule",
+			factory:    cc.GenRuleFactory,
+		},
+		{
+			moduleType: "java_genrule",
+			factory:    java.GenRuleFactory,
+		},
+		{
+			moduleType: "java_genrule_host",
+			factory:    java.GenRuleFactoryHost,
+		},
+	}
+
+	bp := `%s {
     name: "foo.tools",
     out: ["foo_tool.out", "foo_tool2.out"],
     srcs: ["foo_tool.in"],
@@ -91,144 +158,381 @@
     bazel_module: { bp2build_available: true },
 }
 
-genrule {
+%s {
     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{
-				makeBazelTarget("genrule", "foo", attrNameToString{
-					"cmd":   `"$(locations :foo.tools) -s $(OUTS) $(SRCS)"`,
-					"outs":  `["foo.out"]`,
-					"srcs":  `["foo.in"]`,
-					"tools": `[":foo.tools"]`,
-				}),
-				makeBazelTarget("genrule", "foo.tools", attrNameToString{
-					"cmd": `"cp $(SRCS) $(OUTS)"`,
-					"outs": `[
+}`
+
+	for _, tc := range testCases {
+		fooAttrs := attrNameToString{
+			"cmd":   `"$(locations :foo.tools) -s $(OUTS) $(SRCS)"`,
+			"outs":  `["foo.out"]`,
+			"srcs":  `["foo.in"]`,
+			"tools": `[":foo.tools"]`,
+		}
+		fooToolsAttrs := attrNameToString{
+			"cmd": `"cp $(SRCS) $(OUTS)"`,
+			"outs": `[
         "foo_tool.out",
         "foo_tool2.out",
     ]`,
-					"srcs": `["foo_tool.in"]`,
-				}),
-			},
+			"srcs": `["foo_tool.in"]`,
+		}
+
+		if tc.moduleType == "java_genrule_host" {
+			compatibilityAttrs := `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`
+			fooAttrs["target_compatible_with"] = compatibilityAttrs
+			fooToolsAttrs["target_compatible_with"] = compatibilityAttrs
+		}
+
+		expectedBazelTargets := []string{
+			makeBazelTarget("genrule", "foo", fooAttrs),
+			makeBazelTarget("genrule", "foo.tools", fooToolsAttrs),
+		}
+
+		t.Run(tc.moduleType, func(t *testing.T) {
+			runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+				bp2buildTestCase{
+					moduleTypeUnderTest:        tc.moduleType,
+					moduleTypeUnderTestFactory: tc.factory,
+					blueprint:                  fmt.Sprintf(bp, tc.moduleType, tc.moduleType),
+					expectedBazelTargets:       expectedBazelTargets,
+				})
+		})
+	}
+}
+
+func TestGenruleLocationsAbsoluteLabel(t *testing.T) {
+	testCases := []struct {
+		moduleType string
+		factory    android.ModuleFactory
+	}{
+		{
+			moduleType: "genrule",
+			factory:    genrule.GenRuleFactory,
 		},
 		{
-			description: "genrule using $(locations //absolute:label)",
-			blueprint: `genrule {
+			moduleType: "cc_genrule",
+			factory:    cc.GenRuleFactory,
+		},
+		{
+			moduleType: "java_genrule",
+			factory:    java.GenRuleFactory,
+		},
+		{
+			moduleType: "java_genrule_host",
+			factory:    java.GenRuleFactoryHost,
+		},
+	}
+
+	bp := `%s {
     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{
-				makeBazelTarget("genrule", "foo", attrNameToString{
-					"cmd":   `"$(locations //other:foo.tool) -s $(OUTS) $(SRCS)"`,
-					"outs":  `["foo.out"]`,
-					"srcs":  `["foo.in"]`,
-					"tools": `["//other:foo.tool"]`,
-				}),
-			},
-			filesystem: otherGenruleBp,
+}`
+
+	for _, tc := range testCases {
+		moduleAttrs := attrNameToString{
+			"cmd":   `"$(locations //other:foo.tool) -s $(OUTS) $(SRCS)"`,
+			"outs":  `["foo.out"]`,
+			"srcs":  `["foo.in"]`,
+			"tools": `["//other:foo.tool"]`,
+		}
+
+		if tc.moduleType == "java_genrule_host" {
+			moduleAttrs["target_compatible_with"] = `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`
+		}
+
+		expectedBazelTargets := []string{
+			makeBazelTarget("genrule", "foo", moduleAttrs),
+		}
+
+		t.Run(tc.moduleType, func(t *testing.T) {
+			runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+				bp2buildTestCase{
+					moduleTypeUnderTest:        tc.moduleType,
+					moduleTypeUnderTestFactory: tc.factory,
+					blueprint:                  fmt.Sprintf(bp, tc.moduleType),
+					expectedBazelTargets:       expectedBazelTargets,
+					filesystem:                 otherGenruleBp(tc.moduleType),
+				})
+		})
+	}
+}
+
+func TestGenruleSrcsLocationsAbsoluteLabel(t *testing.T) {
+	testCases := []struct {
+		moduleType string
+		factory    android.ModuleFactory
+	}{
+		{
+			moduleType: "genrule",
+			factory:    genrule.GenRuleFactory,
 		},
 		{
-			description: "genrule srcs using $(locations //absolute:label)",
-			blueprint: `genrule {
+			moduleType: "cc_genrule",
+			factory:    cc.GenRuleFactory,
+		},
+		{
+			moduleType: "java_genrule",
+			factory:    java.GenRuleFactory,
+		},
+		{
+			moduleType: "java_genrule_host",
+			factory:    java.GenRuleFactoryHost,
+		},
+	}
+
+	bp := `%s {
     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{
-				makeBazelTarget("genrule", "foo", attrNameToString{
-					"cmd":   `"$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)"`,
-					"outs":  `["foo.out"]`,
-					"srcs":  `["//other:other.tool"]`,
-					"tools": `["//other:foo.tool"]`,
-				}),
-			},
-			filesystem: otherGenruleBp,
+}`
+
+	for _, tc := range testCases {
+		moduleAttrs := attrNameToString{
+			"cmd":   `"$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)"`,
+			"outs":  `["foo.out"]`,
+			"srcs":  `["//other:other.tool"]`,
+			"tools": `["//other:foo.tool"]`,
+		}
+
+		if tc.moduleType == "java_genrule_host" {
+			moduleAttrs["target_compatible_with"] = `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`
+		}
+
+		expectedBazelTargets := []string{
+			makeBazelTarget("genrule", "foo", moduleAttrs),
+		}
+
+		t.Run(tc.moduleType, func(t *testing.T) {
+			runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+				bp2buildTestCase{
+					moduleTypeUnderTest:        tc.moduleType,
+					moduleTypeUnderTestFactory: tc.factory,
+					blueprint:                  fmt.Sprintf(bp, tc.moduleType),
+					expectedBazelTargets:       expectedBazelTargets,
+					filesystem:                 otherGenruleBp(tc.moduleType),
+				})
+		})
+	}
+}
+
+func TestGenruleLocationLabelShouldSubstituteFirstToolLabel(t *testing.T) {
+	testCases := []struct {
+		moduleType string
+		factory    android.ModuleFactory
+	}{
+		{
+			moduleType: "genrule",
+			factory:    genrule.GenRuleFactory,
 		},
 		{
-			description: "genrule using $(location) label should substitute first tool label automatically",
-			blueprint: `genrule {
+			moduleType: "cc_genrule",
+			factory:    cc.GenRuleFactory,
+		},
+		{
+			moduleType: "java_genrule",
+			factory:    java.GenRuleFactory,
+		},
+		{
+			moduleType: "java_genrule_host",
+			factory:    java.GenRuleFactoryHost,
+		},
+	}
+
+	bp := `%s {
     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{
-				makeBazelTarget("genrule", "foo", attrNameToString{
-					"cmd":  `"$(location //other:foo.tool) -s $(OUTS) $(SRCS)"`,
-					"outs": `["foo.out"]`,
-					"srcs": `["foo.in"]`,
-					"tools": `[
+}`
+
+	for _, tc := range testCases {
+		moduleAttrs := attrNameToString{
+			"cmd":  `"$(location //other:foo.tool) -s $(OUTS) $(SRCS)"`,
+			"outs": `["foo.out"]`,
+			"srcs": `["foo.in"]`,
+			"tools": `[
         "//other:foo.tool",
         "//other:other.tool",
     ]`,
-				}),
-			},
-			filesystem: otherGenruleBp,
+		}
+
+		if tc.moduleType == "java_genrule_host" {
+			moduleAttrs["target_compatible_with"] = `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`
+		}
+
+		expectedBazelTargets := []string{
+			makeBazelTarget("genrule", "foo", moduleAttrs),
+		}
+
+		t.Run(tc.moduleType, func(t *testing.T) {
+			runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+				bp2buildTestCase{
+					moduleTypeUnderTest:        tc.moduleType,
+					moduleTypeUnderTestFactory: tc.factory,
+					blueprint:                  fmt.Sprintf(bp, tc.moduleType),
+					expectedBazelTargets:       expectedBazelTargets,
+					filesystem:                 otherGenruleBp(tc.moduleType),
+				})
+		})
+	}
+}
+
+func TestGenruleLocationsLabelShouldSubstituteFirstToolLabel(t *testing.T) {
+	testCases := []struct {
+		moduleType string
+		factory    android.ModuleFactory
+	}{
+		{
+			moduleType: "genrule",
+			factory:    genrule.GenRuleFactory,
 		},
 		{
-			description: "genrule using $(locations) label should substitute first tool label automatically",
-			blueprint: `genrule {
+			moduleType: "cc_genrule",
+			factory:    cc.GenRuleFactory,
+		},
+		{
+			moduleType: "java_genrule",
+			factory:    java.GenRuleFactory,
+		},
+		{
+			moduleType: "java_genrule_host",
+			factory:    java.GenRuleFactoryHost,
+		},
+	}
+
+	bp := `%s {
     name: "foo",
     out: ["foo.out"],
     srcs: ["foo.in"],
-    tools: [":foo.tool", ":other.tool"],
+    tool_files: [":foo.tool", ":other.tool"],
     cmd: "$(locations) -s $(out) $(in)",
     bazel_module: { bp2build_available: true },
-}`,
-			expectedBazelTargets: []string{
-				makeBazelTarget("genrule", "foo", attrNameToString{
-					"cmd":  `"$(locations //other:foo.tool) -s $(OUTS) $(SRCS)"`,
-					"outs": `["foo.out"]`,
-					"srcs": `["foo.in"]`,
-					"tools": `[
+}`
+
+	for _, tc := range testCases {
+		moduleAttrs := attrNameToString{
+			"cmd":  `"$(locations //other:foo.tool) -s $(OUTS) $(SRCS)"`,
+			"outs": `["foo.out"]`,
+			"srcs": `["foo.in"]`,
+			"tools": `[
         "//other:foo.tool",
         "//other:other.tool",
     ]`,
-				}),
-			},
-			filesystem: otherGenruleBp,
+		}
+
+		if tc.moduleType == "java_genrule_host" {
+			moduleAttrs["target_compatible_with"] = `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`
+		}
+
+		expectedBazelTargets := []string{
+			makeBazelTarget("genrule", "foo", moduleAttrs),
+		}
+
+		t.Run(tc.moduleType, func(t *testing.T) {
+			runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+				bp2buildTestCase{
+					moduleTypeUnderTest:        tc.moduleType,
+					moduleTypeUnderTestFactory: tc.factory,
+					blueprint:                  fmt.Sprintf(bp, tc.moduleType),
+					expectedBazelTargets:       expectedBazelTargets,
+					filesystem:                 otherGenruleBp(tc.moduleType),
+				})
+		})
+	}
+}
+
+func TestGenruleWithoutToolsOrToolFiles(t *testing.T) {
+	testCases := []struct {
+		moduleType string
+		factory    android.ModuleFactory
+	}{
+		{
+			moduleType: "genrule",
+			factory:    genrule.GenRuleFactory,
 		},
 		{
-			description: "genrule without tools or tool_files can convert successfully",
-			blueprint: `genrule {
+			moduleType: "cc_genrule",
+			factory:    cc.GenRuleFactory,
+		},
+		{
+			moduleType: "java_genrule",
+			factory:    java.GenRuleFactory,
+		},
+		{
+			moduleType: "java_genrule_host",
+			factory:    java.GenRuleFactoryHost,
+		},
+	}
+
+	bp := `%s {
     name: "foo",
     out: ["foo.out"],
     srcs: ["foo.in"],
     cmd: "cp $(in) $(out)",
     bazel_module: { bp2build_available: true },
-}`,
-			expectedBazelTargets: []string{
-				makeBazelTarget("genrule", "foo", attrNameToString{
-					"cmd":  `"cp $(SRCS) $(OUTS)"`,
-					"outs": `["foo.out"]`,
-					"srcs": `["foo.in"]`,
-				}),
-			},
-		},
-	}
+}`
 
-	for _, testCase := range testCases {
-		t.Run(testCase.description, func(t *testing.T) {
-			runGenruleTestCase(t, testCase)
+	for _, tc := range testCases {
+		moduleAttrs := attrNameToString{
+			"cmd":  `"cp $(SRCS) $(OUTS)"`,
+			"outs": `["foo.out"]`,
+			"srcs": `["foo.in"]`,
+		}
+
+		if tc.moduleType == "java_genrule_host" {
+			moduleAttrs["target_compatible_with"] = `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`
+		}
+
+		expectedBazelTargets := []string{
+			makeBazelTarget("genrule", "foo", moduleAttrs),
+		}
+
+		t.Run(tc.moduleType, func(t *testing.T) {
+			runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {},
+				bp2buildTestCase{
+					moduleTypeUnderTest:        tc.moduleType,
+					moduleTypeUnderTestFactory: tc.factory,
+					blueprint:                  fmt.Sprintf(bp, tc.moduleType),
+					expectedBazelTargets:       expectedBazelTargets,
+				})
 		})
 	}
 }
 
-func TestBp2BuildInlinesDefaults(t *testing.T) {
+func TestGenruleBp2BuildInlinesDefaults(t *testing.T) {
 	testCases := []bp2buildTestCase{
 		{
 			description: "genrule applies properties from a genrule_defaults dependency if not specified",
diff --git a/bp2build/java_binary_host_conversion_test.go b/bp2build/java_binary_host_conversion_test.go
new file mode 100644
index 0000000..65136d9
--- /dev/null
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -0,0 +1,69 @@
+// 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"
+	"android/soong/java"
+)
+
+func runJavaBinaryHostTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	(&tc).moduleTypeUnderTest = "java_binary_host"
+	(&tc).moduleTypeUnderTestFactory = java.BinaryHostFactory
+	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("cc_library_host_shared", cc.LibraryHostSharedFactory)
+	}, tc)
+}
+
+var fs = map[string]string{
+	"test.mf": "Main-Class: com.android.test.MainClass",
+	"other/Android.bp": `cc_library_host_shared {
+    name: "jni-lib-1",
+    stl: "none",
+}`,
+}
+
+func TestJavaBinaryHost(t *testing.T) {
+	runJavaBinaryHostTestCase(t, bp2buildTestCase{
+		description: "java_binary_host with srcs, exclude_srcs, jni_libs, javacflags, and manifest.",
+		filesystem:  fs,
+		blueprint: `java_binary_host {
+    name: "java-binary-host-1",
+    srcs: ["a.java", "b.java"],
+    exclude_srcs: ["b.java"],
+    manifest: "test.mf",
+    jni_libs: ["jni-lib-1"],
+    javacflags: ["-Xdoclint:all/protected"],
+    bazel_module: { bp2build_available: true },
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("java_binary", "java-binary-host-1", attrNameToString{
+				"srcs":       `["a.java"]`,
+				"main_class": `"com.android.test.MainClass"`,
+				"deps":       `["//other:jni-lib-1"]`,
+				"jvm_flags":  `["-Djava.library.path=$${RUNPATH}other"]`,
+				"javacopts":  `["-Xdoclint:all/protected"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/java_import_conversion_test.go b/bp2build/java_import_conversion_test.go
new file mode 100644
index 0000000..2f7211c
--- /dev/null
+++ b/bp2build/java_import_conversion_test.go
@@ -0,0 +1,52 @@
+// Copyright 2022 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/java"
+
+	"testing"
+)
+
+func runJavaImportTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerJavaImportModuleTypes, tc)
+}
+
+func registerJavaImportModuleTypes(ctx android.RegistrationContext) {
+}
+
+func TestMinimalJavaImport(t *testing.T) {
+	runJavaImportTestCase(t, bp2buildTestCase{
+		description:                "Java import - simple example",
+		moduleTypeUnderTest:        "java_import",
+		moduleTypeUnderTestFactory: java.ImportFactory,
+		filesystem: map[string]string{
+			"import.jar": "",
+		},
+		blueprint: `
+java_import {
+        name: "example_import",
+        jars: ["import.jar"],
+        bazel_module: { bp2build_available: true },
+}
+`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("java_import", "example_import", attrNameToString{
+				"jars": `["import.jar"]`,
+			}),
+		}})
+}
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
new file mode 100644
index 0000000..5c65ec2
--- /dev/null
+++ b/bp2build/java_library_conversion_test.go
@@ -0,0 +1,57 @@
+// 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/java"
+)
+
+func runJavaLibraryTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	(&tc).moduleTypeUnderTest = "java_library"
+	(&tc).moduleTypeUnderTestFactory = java.LibraryFactory
+	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+}
+
+func TestJavaLibrary(t *testing.T) {
+	runJavaLibraryTestCase(t, bp2buildTestCase{
+		description: "java_library with srcs, exclude_srcs and libs",
+		blueprint: `java_library {
+    name: "java-lib-1",
+    srcs: ["a.java", "b.java"],
+    exclude_srcs: ["b.java"],
+    libs: ["java-lib-2"],
+    bazel_module: { bp2build_available: true },
+}
+
+java_library {
+    name: "java-lib-2",
+    srcs: ["b.java"],
+    bazel_module: { bp2build_available: true },
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("java_library", "java-lib-1", attrNameToString{
+				"srcs": `["a.java"]`,
+				"deps": `[":java-lib-2"]`,
+			}),
+			makeBazelTarget("java_library", "java-lib-2", attrNameToString{
+				"srcs": `["b.java"]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/java_library_host_conversion_test.go b/bp2build/java_library_host_conversion_test.go
new file mode 100644
index 0000000..73abdd2
--- /dev/null
+++ b/bp2build/java_library_host_conversion_test.go
@@ -0,0 +1,65 @@
+// 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/java"
+)
+
+func runJavaLibraryHostTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	(&tc).moduleTypeUnderTest = "java_library_host"
+	(&tc).moduleTypeUnderTestFactory = java.LibraryHostFactory
+	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+}
+
+func TestJavaLibraryHost(t *testing.T) {
+	runJavaLibraryHostTestCase(t, bp2buildTestCase{
+		description: "java_library_host with srcs, exclude_srcs and libs",
+		blueprint: `java_library_host {
+    name: "java-lib-host-1",
+    srcs: ["a.java", "b.java"],
+    exclude_srcs: ["b.java"],
+    libs: ["java-lib-host-2"],
+    bazel_module: { bp2build_available: true },
+}
+
+java_library_host {
+    name: "java-lib-host-2",
+    srcs: ["c.java"],
+    bazel_module: { bp2build_available: true },
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("java_library", "java-lib-host-1", attrNameToString{
+				"srcs": `["a.java"]`,
+				"deps": `[":java-lib-host-2"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+			makeBazelTarget("java_library", "java-lib-host-2", attrNameToString{
+				"srcs": `["c.java"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/java_plugin_conversion_test.go b/bp2build/java_plugin_conversion_test.go
new file mode 100644
index 0000000..ff13bb0
--- /dev/null
+++ b/bp2build/java_plugin_conversion_test.go
@@ -0,0 +1,72 @@
+// 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/java"
+)
+
+func runJavaPluginTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	(&tc).moduleTypeUnderTest = "java_plugin"
+	(&tc).moduleTypeUnderTestFactory = java.PluginFactory
+	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("java_library", java.LibraryFactory)
+	}, tc)
+}
+
+func TestJavaPlugin(t *testing.T) {
+	runJavaPluginTestCase(t, bp2buildTestCase{
+		description: "java_plugin with srcs, libs, static_libs",
+		blueprint: `java_plugin {
+    name: "java-plug-1",
+    srcs: ["a.java", "b.java"],
+    libs: ["java-lib-1"],
+    static_libs: ["java-lib-2"],
+    bazel_module: { bp2build_available: true },
+}
+
+java_library {
+    name: "java-lib-1",
+    srcs: ["b.java"],
+    bazel_module: { bp2build_available: false },
+}
+
+java_library {
+    name: "java-lib-2",
+    srcs: ["c.java"],
+    bazel_module: { bp2build_available: false },
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("java_plugin", "java-plug-1", attrNameToString{
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+				"deps": `[
+        ":java-lib-1",
+        ":java-lib-2",
+    ]`,
+				"srcs": `[
+        "a.java",
+        "b.java",
+    ]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 1cc4143..8a0b1c9 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -1,62 +1,144 @@
 package bp2build
 
 import (
-	"android/soong/android"
 	"fmt"
+	"os"
+	"path/filepath"
 	"strings"
+
+	"android/soong/android"
+	"android/soong/shared"
+	"android/soong/ui/metrics/bp2build_metrics_proto"
+	"github.com/google/blueprint"
 )
 
 // Simple metrics struct to collect information about a Blueprint to BUILD
 // conversion process.
 type CodegenMetrics struct {
 	// Total number of Soong modules converted to generated targets
-	generatedModuleCount int
+	generatedModuleCount uint64
 
 	// Total number of Soong modules converted to handcrafted targets
-	handCraftedModuleCount int
+	handCraftedModuleCount uint64
 
 	// Total number of unconverted Soong modules
-	unconvertedModuleCount int
+	unconvertedModuleCount uint64
 
 	// Counts of generated Bazel targets per Bazel rule class
-	ruleClassCount map[string]int
+	ruleClassCount map[string]uint64
 
+	// List of modules with unconverted deps
+	// NOTE: NOT in the .proto
 	moduleWithUnconvertedDepsMsgs []string
 
+	// List of modules with missing deps
+	// NOTE: NOT in the .proto
+	moduleWithMissingDepsMsgs []string
+
+	// List of converted modules
 	convertedModules []string
+
+	// Counts of converted modules by module type.
+	convertedModuleTypeCount map[string]uint64
+
+	// Counts of total modules by module type.
+	totalModuleTypeCount map[string]uint64
+}
+
+// Serialize returns the protoized version of CodegenMetrics: bp2build_metrics_proto.Bp2BuildMetrics
+func (metrics *CodegenMetrics) Serialize() bp2build_metrics_proto.Bp2BuildMetrics {
+	return bp2build_metrics_proto.Bp2BuildMetrics{
+		GeneratedModuleCount:     metrics.generatedModuleCount,
+		HandCraftedModuleCount:   metrics.handCraftedModuleCount,
+		UnconvertedModuleCount:   metrics.unconvertedModuleCount,
+		RuleClassCount:           metrics.ruleClassCount,
+		ConvertedModules:         metrics.convertedModules,
+		ConvertedModuleTypeCount: metrics.convertedModuleTypeCount,
+		TotalModuleTypeCount:     metrics.totalModuleTypeCount,
+	}
 }
 
 // Print the codegen metrics to stdout.
 func (metrics *CodegenMetrics) Print() {
-	generatedTargetCount := 0
+	generatedTargetCount := uint64(0)
 	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 With %d modules with unconverted deps \n\t%s",
+		`[bp2build] Converted %d Android.bp modules to %d total generated BUILD targets. Included %d handcrafted BUILD targets. There are %d total Android.bp modules.
+%d converted modules have unconverted deps:
+	%s
+%d converted modules have missing deps:
+	%s
+`,
+		metrics.generatedModuleCount,
 		generatedTargetCount,
 		metrics.handCraftedModuleCount,
 		metrics.TotalModuleCount(),
 		len(metrics.moduleWithUnconvertedDepsMsgs),
-		strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"))
+		strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"),
+		len(metrics.moduleWithMissingDepsMsgs),
+		strings.Join(metrics.moduleWithMissingDepsMsgs, "\n\t"),
+	)
+}
+
+const bp2buildMetricsFilename = "bp2build_metrics.pb"
+
+// fail prints $PWD to stderr, followed by the given printf string and args (vals),
+// then the given alert, and then exits with 1 for failure
+func fail(err error, alertFmt string, vals ...interface{}) {
+	cwd, wderr := os.Getwd()
+	if wderr != nil {
+		cwd = "FAILED TO GET $PWD: " + wderr.Error()
+	}
+	fmt.Fprintf(os.Stderr, "\nIn "+cwd+":\n"+alertFmt+"\n"+err.Error()+"\n", vals...)
+	os.Exit(1)
+}
+
+// Write the bp2build-protoized codegen metrics into the given directory
+func (metrics *CodegenMetrics) Write(dir string) {
+	if _, err := os.Stat(dir); os.IsNotExist(err) {
+		// The metrics dir doesn't already exist, so create it (and parents)
+		if err := os.MkdirAll(dir, 0755); err != nil { // rx for all; w for user
+			fail(err, "Failed to `mkdir -p` %s", dir)
+		}
+	} else if err != nil {
+		fail(err, "Failed to `stat` %s", dir)
+	}
+	metricsFile := filepath.Join(dir, bp2buildMetricsFilename)
+	if err := metrics.dump(metricsFile); err != nil {
+		fail(err, "Error outputting %s", metricsFile)
+	}
+	if _, err := os.Stat(metricsFile); err != nil {
+		fail(err, "MISSING BP2BUILD METRICS OUTPUT: Failed to `stat` %s", metricsFile)
+	} else {
+		fmt.Printf("\nWrote bp2build metrics to: %s\n", metricsFile)
+	}
 }
 
 func (metrics *CodegenMetrics) IncrementRuleClassCount(ruleClass string) {
 	metrics.ruleClassCount[ruleClass] += 1
 }
 
-func (metrics *CodegenMetrics) IncrementUnconvertedCount() {
+func (metrics *CodegenMetrics) AddUnconvertedModule(moduleType string) {
 	metrics.unconvertedModuleCount += 1
+	metrics.totalModuleTypeCount[moduleType] += 1
 }
 
-func (metrics *CodegenMetrics) TotalModuleCount() int {
+func (metrics *CodegenMetrics) TotalModuleCount() uint64 {
 	return metrics.handCraftedModuleCount +
 		metrics.generatedModuleCount +
 		metrics.unconvertedModuleCount
 }
 
+// Dump serializes the metrics to the given filename
+func (metrics *CodegenMetrics) dump(filename string) (err error) {
+	ser := metrics.Serialize()
+	return shared.Save(&ser, filename)
+}
+
 type ConversionType int
 
 const (
@@ -64,10 +146,12 @@
 	Handcrafted
 )
 
-func (metrics *CodegenMetrics) AddConvertedModule(moduleName string, conversionType ConversionType) {
+func (metrics *CodegenMetrics) AddConvertedModule(m blueprint.Module, moduleType string, conversionType ConversionType) {
 	// Undo prebuilt_ module name prefix modifications
-	moduleName = android.RemoveOptionalPrebuiltPrefix(moduleName)
+	moduleName := android.RemoveOptionalPrebuiltPrefix(m.Name())
 	metrics.convertedModules = append(metrics.convertedModules, moduleName)
+	metrics.convertedModuleTypeCount[moduleType] += 1
+	metrics.totalModuleTypeCount[moduleType] += 1
 
 	if conversionType == Handcrafted {
 		metrics.handCraftedModuleCount += 1
diff --git a/bp2build/performance_test.go b/bp2build/performance_test.go
index 3283952..c4bbae2 100644
--- a/bp2build/performance_test.go
+++ b/bp2build/performance_test.go
@@ -29,6 +29,10 @@
 	"testing"
 )
 
+const (
+	performance_test_dir = "."
+)
+
 func genCustomModule(i int, convert bool) string {
 	var conversionString string
 	if convert {
@@ -76,34 +80,83 @@
 	return strings.Join(bp, "\n\n")
 }
 
+type testConfig struct {
+	config     android.Config
+	ctx        *android.TestContext
+	codegenCtx *CodegenContext
+}
+
+func (tc testConfig) parse() []error {
+	_, errs := tc.ctx.ParseFileList(performance_test_dir, []string{"Android.bp"})
+	return errs
+}
+
+func (tc testConfig) resolveDependencies() []error {
+	_, errs := tc.ctx.ResolveDependencies(tc.config)
+	return errs
+}
+
+func (tc testConfig) convert() {
+	generateBazelTargetsForDir(tc.codegenCtx, performance_test_dir)
+}
+
+func setup(builddir string, tcSize float64) testConfig {
+	config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
+	ctx := android.NewTestContext(config)
+
+	registerCustomModuleForBp2buildConversion(ctx)
+	codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+	return testConfig{
+		config,
+		ctx,
+		codegenCtx,
+	}
+}
+
 var pctToConvert = []float64{0.0, 0.01, 0.05, 0.10, 0.25, 0.5, 0.75, 1.0}
 
+// This is not intended to test performance, but to verify performance infra continues to work
+func TestConvertManyModulesFull(t *testing.T) {
+	for _, tcSize := range pctToConvert {
+
+		t.Run(fmt.Sprintf("pctConverted %f", tcSize), func(t *testing.T) {
+			testConfig := setup(buildDir, tcSize)
+
+			errs := testConfig.parse()
+			if len(errs) > 0 {
+				t.Fatalf("Unexpected errors: %s", errs)
+			}
+
+			errs = testConfig.resolveDependencies()
+			if len(errs) > 0 {
+				t.Fatalf("Unexpected errors: %s", errs)
+			}
+
+			testConfig.convert()
+		})
+	}
+}
+
 func BenchmarkManyModulesFull(b *testing.B) {
-	dir := "."
 	for _, tcSize := range pctToConvert {
 
 		b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
 			for n := 0; n < b.N; n++ {
 				b.StopTimer()
-				// setup we don't want to measure
-				config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
-				ctx := android.NewTestContext(config)
-
-				registerCustomModuleForBp2buildConversion(ctx)
-				codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+				testConfig := setup(buildDir, tcSize)
 
 				b.StartTimer()
-				_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+				errs := testConfig.parse()
 				if len(errs) > 0 {
 					b.Fatalf("Unexpected errors: %s", errs)
 				}
 
-				_, errs = ctx.ResolveDependencies(config)
+				errs = testConfig.resolveDependencies()
 				if len(errs) > 0 {
 					b.Fatalf("Unexpected errors: %s", errs)
 				}
 
-				generateBazelTargetsForDir(codegenCtx, dir)
+				testConfig.convert()
 				b.StopTimer()
 			}
 		})
@@ -111,63 +164,53 @@
 }
 
 func BenchmarkManyModulesResolveDependencies(b *testing.B) {
-	dir := "."
 	for _, tcSize := range pctToConvert {
 
 		b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
 			for n := 0; n < b.N; n++ {
 				b.StopTimer()
 				// setup we don't want to measure
-				config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
-				ctx := android.NewTestContext(config)
+				testConfig := setup(buildDir, tcSize)
 
-				registerCustomModuleForBp2buildConversion(ctx)
-				codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-
-				_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+				errs := testConfig.parse()
 				if len(errs) > 0 {
 					b.Fatalf("Unexpected errors: %s", errs)
 				}
 
 				b.StartTimer()
-				_, errs = ctx.ResolveDependencies(config)
+				errs = testConfig.resolveDependencies()
 				b.StopTimer()
 				if len(errs) > 0 {
 					b.Fatalf("Unexpected errors: %s", errs)
 				}
 
-				generateBazelTargetsForDir(codegenCtx, dir)
+				testConfig.convert()
 			}
 		})
 	}
 }
 
 func BenchmarkManyModulesGenerateBazelTargetsForDir(b *testing.B) {
-	dir := "."
 	for _, tcSize := range pctToConvert {
 
 		b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
 			for n := 0; n < b.N; n++ {
 				b.StopTimer()
 				// setup we don't want to measure
-				config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
-				ctx := android.NewTestContext(config)
+				testConfig := setup(buildDir, tcSize)
 
-				registerCustomModuleForBp2buildConversion(ctx)
-				codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-
-				_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+				errs := testConfig.parse()
 				if len(errs) > 0 {
 					b.Fatalf("Unexpected errors: %s", errs)
 				}
 
-				_, errs = ctx.ResolveDependencies(config)
+				errs = testConfig.resolveDependencies()
 				if len(errs) > 0 {
 					b.Fatalf("Unexpected errors: %s", errs)
 				}
 
 				b.StartTimer()
-				generateBazelTargetsForDir(codegenCtx, dir)
+				testConfig.convert()
 				b.StopTimer()
 			}
 		})
diff --git a/bp2build/prebuilt_etc_conversion_test.go b/bp2build/prebuilt_etc_conversion_test.go
index 1189309..3a5d5bb 100644
--- a/bp2build/prebuilt_etc_conversion_test.go
+++ b/bp2build/prebuilt_etc_conversion_test.go
@@ -25,7 +25,6 @@
 	t.Helper()
 	(&tc).moduleTypeUnderTest = "prebuilt_etc"
 	(&tc).moduleTypeUnderTestFactory = etc.PrebuiltEtcFactory
-	(&tc).moduleTypeUnderTestBp2BuildMutator = etc.PrebuiltEtcBp2Build
 	runBp2BuildTestCase(t, registerPrebuiltEtcModuleTypes, tc)
 }
 
@@ -87,3 +86,45 @@
 				"sub_dir": `"tz"`,
 			})}})
 }
+
+func TestPrebuiltEtcArchAndTargetVariant(t *testing.T) {
+	runPrebuiltEtcTestCase(t, bp2buildTestCase{
+		description: "prebuilt_etc - arch variant",
+		filesystem:  map[string]string{},
+		blueprint: `
+prebuilt_etc {
+    name: "apex_tz_version",
+    src: "version/tz_version",
+    filename: "tz_version",
+    sub_dir: "tz",
+    installable: false,
+    arch: {
+      arm: {
+        src: "arm",
+      },
+      arm64: {
+        src: "darwin_or_arm64",
+      },
+    },
+    target: {
+      darwin: {
+        src: "darwin_or_arm64",
+      }
+    },
+}
+`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("prebuilt_etc", "apex_tz_version", attrNameToString{
+				"filename":    `"tz_version"`,
+				"installable": `False`,
+				"src": `select({
+        "//build/bazel/platforms/os_arch:android_arm": "arm",
+        "//build/bazel/platforms/os_arch:android_arm64": "darwin_or_arm64",
+        "//build/bazel/platforms/os_arch:darwin_arm64": "darwin_or_arm64",
+        "//build/bazel/platforms/os_arch:darwin_x86_64": "darwin_or_arm64",
+        "//build/bazel/platforms/os_arch:linux_bionic_arm64": "darwin_or_arm64",
+        "//conditions:default": "version/tz_version",
+    })`,
+				"sub_dir": `"tz"`,
+			})}})
+}
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
index 01b6aa2..dfa11d1 100644
--- a/bp2build/python_binary_conversion_test.go
+++ b/bp2build/python_binary_conversion_test.go
@@ -17,10 +17,9 @@
 
 func TestPythonBinaryHostSimple(t *testing.T) {
 	runBp2BuildTestCaseWithPythonLibraries(t, bp2buildTestCase{
-		description:                        "simple python_binary_host converts to a native py_binary",
-		moduleTypeUnderTest:                "python_binary_host",
-		moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
-		moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+		description:                "simple python_binary_host converts to a native py_binary",
+		moduleTypeUnderTest:        "python_binary_host",
+		moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
 		filesystem: map[string]string{
 			"a.py":           "",
 			"b/c.py":         "",
@@ -40,7 +39,7 @@
     python_library_host {
       name: "bar",
       srcs: ["b/e.py"],
-      bazel_module: { bp2build_available: true },
+      bazel_module: { bp2build_available: false },
     }`,
 		expectedBazelTargets: []string{
 			makeBazelTarget("py_binary", "foo", attrNameToString{
@@ -52,6 +51,10 @@
         "b/c.py",
         "b/d.py",
     ]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
 			}),
 		},
 	})
@@ -59,10 +62,9 @@
 
 func TestPythonBinaryHostPy2(t *testing.T) {
 	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
-		description:                        "py2 python_binary_host",
-		moduleTypeUnderTest:                "python_binary_host",
-		moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
-		moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+		description:                "py2 python_binary_host",
+		moduleTypeUnderTest:        "python_binary_host",
+		moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
 		blueprint: `python_binary_host {
     name: "foo",
     srcs: ["a.py"],
@@ -82,6 +84,10 @@
 			makeBazelTarget("py_binary", "foo", attrNameToString{
 				"python_version": `"PY2"`,
 				"srcs":           `["a.py"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
 			}),
 		},
 	})
@@ -89,10 +95,9 @@
 
 func TestPythonBinaryHostPy3(t *testing.T) {
 	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
-		description:                        "py3 python_binary_host",
-		moduleTypeUnderTest:                "python_binary_host",
-		moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
-		moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+		description:                "py3 python_binary_host",
+		moduleTypeUnderTest:        "python_binary_host",
+		moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
 		blueprint: `python_binary_host {
     name: "foo",
     srcs: ["a.py"],
@@ -112,6 +117,10 @@
 			// python_version is PY3 by default.
 			makeBazelTarget("py_binary", "foo", attrNameToString{
 				"srcs": `["a.py"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
 			}),
 		},
 	})
@@ -119,10 +128,9 @@
 
 func TestPythonBinaryHostArchVariance(t *testing.T) {
 	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
-		description:                        "test arch variants",
-		moduleTypeUnderTest:                "python_binary_host",
-		moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
-		moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+		description:                "test arch variants",
+		moduleTypeUnderTest:        "python_binary_host",
+		moduleTypeUnderTestFactory: python.PythonBinaryHostFactory,
 		filesystem: map[string]string{
 			"dir/arm.py": "",
 			"dir/x86.py": "",
@@ -145,6 +153,10 @@
         "//build/bazel/platforms/arch:x86": ["x86.py"],
         "//conditions:default": [],
     })`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
 			}),
 		},
 	})
diff --git a/bp2build/python_library_conversion_test.go b/bp2build/python_library_conversion_test.go
index e334592..356d52e 100644
--- a/bp2build/python_library_conversion_test.go
+++ b/bp2build/python_library_conversion_test.go
@@ -11,39 +11,69 @@
 // TODO(alexmarquez): Should be lifted into a generic Bp2Build file
 type PythonLibBp2Build func(ctx android.TopDownMutatorContext)
 
-func runPythonLibraryTestCase(t *testing.T, tc bp2buildTestCase) {
+type pythonLibBp2BuildTestCase struct {
+	description          string
+	filesystem           map[string]string
+	blueprint            string
+	expectedBazelTargets []testBazelTarget
+}
+
+func convertPythonLibTestCaseToBp2build_Host(tc pythonLibBp2BuildTestCase) bp2buildTestCase {
+	for i := range tc.expectedBazelTargets {
+		tc.expectedBazelTargets[i].attrs["target_compatible_with"] = `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`
+	}
+
+	return convertPythonLibTestCaseToBp2build(tc)
+}
+
+func convertPythonLibTestCaseToBp2build(tc pythonLibBp2BuildTestCase) bp2buildTestCase {
+	var bp2BuildTargets []string
+	for _, t := range tc.expectedBazelTargets {
+		bp2BuildTargets = append(bp2BuildTargets, makeBazelTarget(t.typ, t.name, t.attrs))
+	}
+	return bp2buildTestCase{
+		description:          tc.description,
+		filesystem:           tc.filesystem,
+		blueprint:            tc.blueprint,
+		expectedBazelTargets: bp2BuildTargets,
+	}
+}
+
+func runPythonLibraryTestCase(t *testing.T, tc pythonLibBp2BuildTestCase) {
 	t.Helper()
-	testCase := tc
+	testCase := convertPythonLibTestCaseToBp2build(tc)
 	testCase.description = fmt.Sprintf(testCase.description, "python_library")
 	testCase.blueprint = fmt.Sprintf(testCase.blueprint, "python_library")
 	testCase.moduleTypeUnderTest = "python_library"
 	testCase.moduleTypeUnderTestFactory = python.PythonLibraryFactory
-	testCase.moduleTypeUnderTestBp2BuildMutator = python.PythonLibraryBp2Build
+
 	runBp2BuildTestCaseSimple(t, testCase)
 }
 
-func runPythonLibraryHostTestCase(t *testing.T, tc bp2buildTestCase) {
+func runPythonLibraryHostTestCase(t *testing.T, tc pythonLibBp2BuildTestCase) {
 	t.Helper()
-	testCase := tc
+	testCase := convertPythonLibTestCaseToBp2build_Host(tc)
 	testCase.description = fmt.Sprintf(testCase.description, "python_library_host")
 	testCase.blueprint = fmt.Sprintf(testCase.blueprint, "python_library_host")
 	testCase.moduleTypeUnderTest = "python_library_host"
 	testCase.moduleTypeUnderTestFactory = python.PythonLibraryHostFactory
-	testCase.moduleTypeUnderTestBp2BuildMutator = python.PythonLibraryHostBp2Build
 	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
 		ctx.RegisterModuleType("python_library", python.PythonLibraryFactory)
 	},
 		testCase)
 }
 
-func runPythonLibraryTestCases(t *testing.T, tc bp2buildTestCase) {
+func runPythonLibraryTestCases(t *testing.T, tc pythonLibBp2BuildTestCase) {
 	t.Helper()
 	runPythonLibraryTestCase(t, tc)
 	runPythonLibraryHostTestCase(t, tc)
 }
 
 func TestSimplePythonLib(t *testing.T) {
-	testCases := []bp2buildTestCase{
+	testCases := []pythonLibBp2BuildTestCase{
 		{
 			description: "simple %s converts to a native py_library",
 			filesystem: map[string]string{
@@ -66,17 +96,21 @@
       srcs: ["b/e.py"],
       bazel_module: { bp2build_available: false },
     }`,
-			expectedBazelTargets: []string{
-				makeBazelTarget("py_library", "foo", attrNameToString{
-					"data": `["files/data.txt"]`,
-					"deps": `[":bar"]`,
-					"srcs": `[
+			expectedBazelTargets: []testBazelTarget{
+				{
+					typ:  "py_library",
+					name: "foo",
+					attrs: attrNameToString{
+						"data": `["files/data.txt"]`,
+						"deps": `[":bar"]`,
+						"srcs": `[
         "a.py",
         "b/c.py",
         "b/d.py",
     ]`,
-					"srcs_version": `"PY3"`,
-				}),
+						"srcs_version": `"PY3"`,
+					},
+				},
 			},
 		},
 		{
@@ -95,11 +129,15 @@
 
     bazel_module: { bp2build_available: true },
 }`,
-			expectedBazelTargets: []string{
-				makeBazelTarget("py_library", "foo", attrNameToString{
-					"srcs":         `["a.py"]`,
-					"srcs_version": `"PY2"`,
-				}),
+			expectedBazelTargets: []testBazelTarget{
+				{
+					typ:  "py_library",
+					name: "foo",
+					attrs: attrNameToString{
+						"srcs":         `["a.py"]`,
+						"srcs_version": `"PY2"`,
+					},
+				},
 			},
 		},
 		{
@@ -118,11 +156,15 @@
 
     bazel_module: { bp2build_available: true },
 }`,
-			expectedBazelTargets: []string{
-				makeBazelTarget("py_library", "foo", attrNameToString{
-					"srcs":         `["a.py"]`,
-					"srcs_version": `"PY3"`,
-				}),
+			expectedBazelTargets: []testBazelTarget{
+				{
+					typ:  "py_library",
+					name: "foo",
+					attrs: attrNameToString{
+						"srcs":         `["a.py"]`,
+						"srcs_version": `"PY3"`,
+					},
+				},
 			},
 		},
 		{
@@ -141,11 +183,15 @@
 
     bazel_module: { bp2build_available: true },
 }`,
-			expectedBazelTargets: []string{
-				// srcs_version is PY2ANDPY3 by default.
-				makeBazelTarget("py_library", "foo", attrNameToString{
-					"srcs": `["a.py"]`,
-				}),
+			expectedBazelTargets: []testBazelTarget{
+				{
+					// srcs_version is PY2ANDPY3 by default.
+					typ:  "py_library",
+					name: "foo",
+					attrs: attrNameToString{
+						"srcs": `["a.py"]`,
+					},
+				},
 			},
 		},
 	}
@@ -158,7 +204,7 @@
 }
 
 func TestPythonArchVariance(t *testing.T) {
-	runPythonLibraryTestCases(t, bp2buildTestCase{
+	runPythonLibraryTestCases(t, pythonLibBp2BuildTestCase{
 		description: "test %s arch variants",
 		filesystem: map[string]string{
 			"dir/arm.py": "",
@@ -175,15 +221,19 @@
 						 },
 					},
 				 }`,
-		expectedBazelTargets: []string{
-			makeBazelTarget("py_library", "foo", attrNameToString{
-				"srcs": `select({
+		expectedBazelTargets: []testBazelTarget{
+			{
+				typ:  "py_library",
+				name: "foo",
+				attrs: attrNameToString{
+					"srcs": `select({
         "//build/bazel/platforms/arch:arm": ["arm.py"],
         "//build/bazel/platforms/arch:x86": ["x86.py"],
         "//conditions:default": [],
     })`,
-				"srcs_version": `"PY3"`,
-			}),
+					"srcs_version": `"PY3"`,
+				},
+			},
 		},
 	})
 }
diff --git a/bp2build/sh_conversion_test.go b/bp2build/sh_conversion_test.go
index 1ca4a0e..ac89087 100644
--- a/bp2build/sh_conversion_test.go
+++ b/bp2build/sh_conversion_test.go
@@ -55,10 +55,30 @@
 
 func TestShBinarySimple(t *testing.T) {
 	runShBinaryTestCase(t, bp2buildTestCase{
-		description:                        "sh_binary test",
-		moduleTypeUnderTest:                "sh_binary",
-		moduleTypeUnderTestFactory:         sh.ShBinaryFactory,
-		moduleTypeUnderTestBp2BuildMutator: sh.ShBinaryBp2Build,
+		description:                "sh_binary test",
+		moduleTypeUnderTest:        "sh_binary",
+		moduleTypeUnderTestFactory: sh.ShBinaryFactory,
+		blueprint: `sh_binary {
+    name: "foo",
+    src: "foo.sh",
+    filename: "foo.exe",
+    sub_dir: "sub",
+    bazel_module: { bp2build_available: true },
+}`,
+		expectedBazelTargets: []string{
+			makeBazelTarget("sh_binary", "foo", attrNameToString{
+				"srcs":     `["foo.sh"]`,
+				"filename": `"foo.exe"`,
+				"sub_dir":  `"sub"`,
+			})},
+	})
+}
+
+func TestShBinaryDefaults(t *testing.T) {
+	runShBinaryTestCase(t, bp2buildTestCase{
+		description:                "sh_binary test",
+		moduleTypeUnderTest:        "sh_binary",
+		moduleTypeUnderTestFactory: sh.ShBinaryFactory,
 		blueprint: `sh_binary {
     name: "foo",
     src: "foo.sh",
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index d21db04..b1e1fb2 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -61,15 +61,14 @@
 `
 
 	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
-		description:                        "soong config variables - soong_config_module_type is supported in bp2build",
-		moduleTypeUnderTest:                "cc_library_static",
-		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-		blueprint:                          bp,
+		description:                "soong config variables - soong_config_module_type is supported in bp2build",
+		moduleTypeUnderTest:        "cc_library_static",
+		moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+		blueprint:                  bp,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo",
     copts = select({
-        "//build/bazel/product_variables:acme__feature1__enabled": ["-DFEATURE1"],
+        "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
         "//conditions:default": ["-DDEFAULT1"],
     }),
     local_includes = ["."],
@@ -107,10 +106,9 @@
 `
 
 	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
-		description:                        "soong config variables - soong_config_module_type_import is supported in bp2build",
-		moduleTypeUnderTest:                "cc_library_static",
-		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		description:                "soong config variables - soong_config_module_type_import is supported in bp2build",
+		moduleTypeUnderTest:        "cc_library_static",
+		moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
 		filesystem: map[string]string{
 			"foo/bar/SoongConfig.bp": configBp,
 		},
@@ -118,7 +116,7 @@
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo",
     copts = select({
-        "//build/bazel/product_variables:acme__feature1__enabled": ["-DFEATURE1"],
+        "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
         "//conditions:default": ["-DDEFAULT1"],
     }),
     local_includes = ["."],
@@ -161,11 +159,10 @@
 `
 
 	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
-		description:                        "soong config variables - generates selects for string vars",
-		moduleTypeUnderTest:                "cc_library_static",
-		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-		blueprint:                          bp,
+		description:                "soong config variables - generates selects for string vars",
+		moduleTypeUnderTest:        "cc_library_static",
+		moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+		blueprint:                  bp,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo",
     copts = select({
@@ -232,11 +229,10 @@
 }`
 
 	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
-		description:                        "soong config variables - generates selects for multiple variable types",
-		moduleTypeUnderTest:                "cc_library_static",
-		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-		blueprint:                          bp,
+		description:                "soong config variables - generates selects for multiple variable types",
+		moduleTypeUnderTest:        "cc_library_static",
+		moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+		blueprint:                  bp,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo",
     copts = select({
@@ -244,10 +240,10 @@
         "//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
         "//conditions:default": ["-DSOC_DEFAULT"],
     }) + select({
-        "//build/bazel/product_variables:acme__feature1__enabled": ["-DFEATURE1"],
+        "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
         "//conditions:default": ["-DDEFAULT1"],
     }) + select({
-        "//build/bazel/product_variables:acme__feature2__enabled": ["-DFEATURE2"],
+        "//build/bazel/product_variables:acme__feature2": ["-DFEATURE2"],
         "//conditions:default": ["-DDEFAULT2"],
     }),
     local_includes = ["."],
@@ -298,11 +294,10 @@
 `
 
 	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
-		description:                        "soong config variables - generates selects for label list attributes",
-		moduleTypeUnderTest:                "cc_library_static",
-		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-		blueprint:                          bp,
+		description:                "soong config variables - generates selects for label list attributes",
+		moduleTypeUnderTest:        "cc_library_static",
+		moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+		blueprint:                  bp,
 		filesystem: map[string]string{
 			"foo/bar/Android.bp": otherDeps,
 		},
@@ -365,15 +360,14 @@
 `
 
 	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
-		description:                        "soong config variables - defaults with a single namespace",
-		moduleTypeUnderTest:                "cc_library_static",
-		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-		blueprint:                          bp,
+		description:                "soong config variables - defaults with a single namespace",
+		moduleTypeUnderTest:        "cc_library_static",
+		moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+		blueprint:                  bp,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "lib",
     copts = select({
-        "//build/bazel/product_variables:vendor_foo__feature__enabled": [
+        "//build/bazel/product_variables:vendor_foo__feature": [
             "-cflag_feature_2",
             "-cflag_feature_1",
         ],
@@ -445,19 +439,18 @@
 `
 
 	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
-		description:                        "soong config variables - multiple defaults with a single namespace",
-		moduleTypeUnderTest:                "cc_library_static",
-		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-		blueprint:                          bp,
+		description:                "soong config variables - multiple defaults with a single namespace",
+		moduleTypeUnderTest:        "cc_library_static",
+		moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+		blueprint:                  bp,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "lib",
     asflags = select({
-        "//build/bazel/product_variables:acme__feature__enabled": ["-asflag_bar"],
+        "//build/bazel/product_variables:acme__feature": ["-asflag_bar"],
         "//conditions:default": ["-asflag_default_bar"],
     }),
     copts = select({
-        "//build/bazel/product_variables:acme__feature__enabled": [
+        "//build/bazel/product_variables:acme__feature": [
             "-cflag_foo",
             "-cflag_bar",
         ],
@@ -472,11 +465,11 @@
 			`cc_library_static(
     name = "lib2",
     asflags = select({
-        "//build/bazel/product_variables:acme__feature__enabled": ["-asflag_bar"],
+        "//build/bazel/product_variables:acme__feature": ["-asflag_bar"],
         "//conditions:default": ["-asflag_default_bar"],
     }),
     copts = select({
-        "//build/bazel/product_variables:acme__feature__enabled": [
+        "//build/bazel/product_variables:acme__feature": [
             "-cflag_bar",
             "-cflag_foo",
         ],
@@ -561,21 +554,20 @@
 `
 
 	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
-		description:                        "soong config variables - defaults with multiple namespaces",
-		moduleTypeUnderTest:                "cc_library_static",
-		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-		blueprint:                          bp,
+		description:                "soong config variables - defaults with multiple namespaces",
+		moduleTypeUnderTest:        "cc_library_static",
+		moduleTypeUnderTestFactory: cc.LibraryStaticFactory,
+		blueprint:                  bp,
 		expectedBazelTargets: []string{`cc_library_static(
     name = "lib",
     copts = select({
-        "//build/bazel/product_variables:vendor_bar__feature__enabled": ["-DVENDOR_BAR_FEATURE"],
+        "//build/bazel/product_variables:vendor_bar__feature": ["-DVENDOR_BAR_FEATURE"],
         "//conditions:default": ["-DVENDOR_BAR_DEFAULT"],
     }) + select({
-        "//build/bazel/product_variables:vendor_foo__feature__enabled": ["-DVENDOR_FOO_FEATURE"],
+        "//build/bazel/product_variables:vendor_foo__feature": ["-DVENDOR_FOO_FEATURE"],
         "//conditions:default": ["-DVENDOR_FOO_DEFAULT"],
     }) + select({
-        "//build/bazel/product_variables:vendor_qux__feature__enabled": ["-DVENDOR_QUX_FEATURE"],
+        "//build/bazel/product_variables:vendor_qux__feature": ["-DVENDOR_QUX_FEATURE"],
         "//conditions:default": ["-DVENDOR_QUX_DEFAULT"],
     }),
     local_includes = ["."],
@@ -652,11 +644,10 @@
 `
 
 	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
-		description:                        "soong config variables - generates selects for library_linking_strategy",
-		moduleTypeUnderTest:                "cc_binary",
-		moduleTypeUnderTestFactory:         cc.BinaryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.BinaryBp2build,
-		blueprint:                          bp,
+		description:                "soong config variables - generates selects for library_linking_strategy",
+		moduleTypeUnderTest:        "cc_binary",
+		moduleTypeUnderTestFactory: cc.BinaryFactory,
+		blueprint:                  bp,
 		filesystem: map[string]string{
 			"foo/bar/Android.bp": otherDeps,
 		},
@@ -733,11 +724,10 @@
 `
 
 	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
-		description:                        "soong config variables - generates selects for library_linking_strategy",
-		moduleTypeUnderTest:                "cc_binary",
-		moduleTypeUnderTestFactory:         cc.BinaryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.BinaryBp2build,
-		blueprint:                          bp,
+		description:                "soong config variables - generates selects for library_linking_strategy",
+		moduleTypeUnderTest:        "cc_binary",
+		moduleTypeUnderTestFactory: cc.BinaryFactory,
+		blueprint:                  bp,
 		filesystem: map[string]string{
 			"foo/bar/Android.bp": otherDeps,
 		},
@@ -821,11 +811,10 @@
 `
 
 	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
-		description:                        "soong config variables - generates selects for library_linking_strategy",
-		moduleTypeUnderTest:                "cc_binary",
-		moduleTypeUnderTestFactory:         cc.BinaryFactory,
-		moduleTypeUnderTestBp2BuildMutator: cc.BinaryBp2build,
-		blueprint:                          bp,
+		description:                "soong config variables - generates selects for library_linking_strategy",
+		moduleTypeUnderTest:        "cc_binary",
+		moduleTypeUnderTestFactory: cc.BinaryFactory,
+		blueprint:                  bp,
 		filesystem: map[string]string{
 			"foo/bar/Android.bp": otherDeps,
 		},
@@ -845,3 +834,152 @@
     srcs = ["main.cc"],
 )`}})
 }
+
+func TestSoongConfigModuleType_ProductVariableConfigWithPlatformConfig(t *testing.T) {
+	bp := `
+soong_config_bool_variable {
+    name: "special_build",
+}
+
+soong_config_module_type {
+    name: "alphabet_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "alphabet_module",
+    bool_variables: ["special_build"],
+    properties: ["enabled"],
+}
+
+alphabet_cc_defaults {
+    name: "alphabet_sample_cc_defaults",
+    soong_config_variables: {
+        special_build: {
+            enabled: true,
+        },
+    },
+}
+
+cc_binary {
+    name: "alphabet_binary",
+    srcs: ["main.cc"],
+    defaults: ["alphabet_sample_cc_defaults"],
+    enabled: false,
+    arch: {
+        x86_64: {
+            enabled: false,
+        },
+    },
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}`
+
+	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
+		description:                "soong config variables - generates selects for library_linking_strategy",
+		moduleTypeUnderTest:        "cc_binary",
+		moduleTypeUnderTestFactory: cc.BinaryFactory,
+		blueprint:                  bp,
+		filesystem:                 map[string]string{},
+		expectedBazelTargets: []string{`cc_binary(
+    name = "alphabet_binary",
+    local_includes = ["."],
+    srcs = ["main.cc"],
+    target_compatible_with = ["//build/bazel/product_variables:alphabet_module__special_build"] + select({
+        "//build/bazel/platforms/os_arch:android_x86_64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:darwin_arm64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:darwin_x86_64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:linux_bionic_x86_64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:linux_glibc_x86_64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:linux_musl_x86_64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:windows_x86_64": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    }),
+)`}})
+}
+
+func TestSoongConfigModuleType_ProductVariableConfigOverridesEnable(t *testing.T) {
+	bp := `
+soong_config_bool_variable {
+    name: "special_build",
+}
+
+soong_config_module_type {
+    name: "alphabet_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "alphabet_module",
+    bool_variables: ["special_build"],
+    properties: ["enabled"],
+}
+
+alphabet_cc_defaults {
+    name: "alphabet_sample_cc_defaults",
+    soong_config_variables: {
+        special_build: {
+            enabled: true,
+        },
+    },
+}
+
+cc_binary {
+    name: "alphabet_binary",
+    srcs: ["main.cc"],
+    defaults: ["alphabet_sample_cc_defaults"],
+    enabled: false,
+}`
+
+	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
+		description:                "soong config variables - generates selects for library_linking_strategy",
+		moduleTypeUnderTest:        "cc_binary",
+		moduleTypeUnderTestFactory: cc.BinaryFactory,
+		blueprint:                  bp,
+		filesystem:                 map[string]string{},
+		expectedBazelTargets: []string{`cc_binary(
+    name = "alphabet_binary",
+    local_includes = ["."],
+    srcs = ["main.cc"],
+    target_compatible_with = ["//build/bazel/product_variables:alphabet_module__special_build"],
+)`}})
+}
+
+func TestSoongConfigModuleType_ProductVariableIgnoredIfEnabledByDefault(t *testing.T) {
+	bp := `
+soong_config_bool_variable {
+    name: "special_build",
+}
+
+soong_config_module_type {
+    name: "alphabet_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "alphabet_module",
+    bool_variables: ["special_build"],
+    properties: ["enabled"],
+}
+
+alphabet_cc_defaults {
+    name: "alphabet_sample_cc_defaults",
+    soong_config_variables: {
+        special_build: {
+            enabled: true,
+        },
+    },
+}
+
+cc_binary {
+    name: "alphabet_binary",
+    srcs: ["main.cc"],
+    defaults: ["alphabet_sample_cc_defaults"],
+}`
+
+	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
+		description:                "soong config variables - generates selects for library_linking_strategy",
+		moduleTypeUnderTest:        "cc_binary",
+		moduleTypeUnderTestFactory: cc.BinaryFactory,
+		blueprint:                  bp,
+		filesystem:                 map[string]string{},
+		expectedBazelTargets: []string{`cc_binary(
+    name = "alphabet_binary",
+    local_includes = ["."],
+    srcs = ["main.cc"],
+)`}})
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index cd84519..53b60fa 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -43,7 +43,7 @@
 	if len(errs) != 1 {
 		return false
 	}
-	if errs[0].Error() == expectedErr.Error() {
+	if strings.Contains(errs[0].Error(), expectedErr.Error()) {
 		return true
 	}
 
@@ -74,16 +74,15 @@
 }
 
 type bp2buildTestCase struct {
-	description                        string
-	moduleTypeUnderTest                string
-	moduleTypeUnderTestFactory         android.ModuleFactory
-	moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
-	blueprint                          string
-	expectedBazelTargets               []string
-	filesystem                         map[string]string
-	dir                                string
-	expectedErr                        error
-	unconvertedDepsMode                unconvertedDepsMode
+	description                string
+	moduleTypeUnderTest        string
+	moduleTypeUnderTestFactory android.ModuleFactory
+	blueprint                  string
+	expectedBazelTargets       []string
+	filesystem                 map[string]string
+	dir                        string
+	expectedErr                error
+	unconvertedDepsMode        unconvertedDepsMode
 }
 
 func runBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc bp2buildTestCase) {
@@ -105,7 +104,6 @@
 	registerModuleTypes(ctx)
 	ctx.RegisterModuleType(tc.moduleTypeUnderTest, tc.moduleTypeUnderTestFactory)
 	ctx.RegisterBp2BuildConfig(bp2buildConfig)
-	ctx.RegisterBp2BuildMutator(tc.moduleTypeUnderTest, tc.moduleTypeUnderTestBp2BuildMutator)
 	ctx.RegisterForBazelConversion()
 
 	_, parseErrs := ctx.ParseFileList(dir, toParse)
@@ -129,14 +127,18 @@
 	codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
 	codegenCtx.unconvertedDepMode = tc.unconvertedDepsMode
 	bazelTargets, errs := generateBazelTargetsForDir(codegenCtx, checkDir)
-	if tc.expectedErr != nil && checkError(t, errs, tc.expectedErr) {
-		return
+	if tc.expectedErr != nil {
+		if checkError(t, errs, tc.expectedErr) {
+			return
+		} else {
+			t.Errorf("Expected error: %q, got: %q", tc.expectedErr, errs)
+		}
 	} else {
 		android.FailIfErrored(t, errs)
 	}
 	if actualCount, expectedCount := len(bazelTargets), len(tc.expectedBazelTargets); actualCount != expectedCount {
-		t.Errorf("%s: Expected %d bazel target, got `%d``",
-			tc.description, expectedCount, actualCount)
+		t.Errorf("%s: Expected %d bazel target (%s), got `%d`` (%s)",
+			tc.description, expectedCount, tc.expectedBazelTargets, actualCount, bazelTargets)
 	} else {
 		for i, target := range bazelTargets {
 			if w, g := tc.expectedBazelTargets[i], target.content; w != g {
@@ -178,6 +180,9 @@
 
 	Arch_paths         []string `android:"path,arch_variant"`
 	Arch_paths_exclude []string `android:"path,arch_variant"`
+
+	// Prop used to indicate this conversion should be 1 module -> multiple targets
+	One_to_many_prop *bool
 }
 
 type customModule struct {
@@ -277,71 +282,65 @@
 	Arch_paths       bazel.LabelListAttribute
 }
 
-func customBp2BuildMutator(ctx android.TopDownMutatorContext) {
-	if m, ok := ctx.Module().(*customModule); ok {
-		if !m.ConvertWithBp2build(ctx) {
-			return
-		}
+func (m *customModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	paths := bazel.LabelListAttribute{}
 
-		paths := bazel.LabelListAttribute{}
+	if p := m.props.One_to_many_prop; p != nil && *p {
+		customBp2buildOneToMany(ctx, m)
+		return
+	}
 
-		for axis, configToProps := range m.GetArchVariantProperties(ctx, &customProps{}) {
-			for config, props := range configToProps {
-				if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
-					paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrcExcludes(ctx, archProps.Arch_paths, archProps.Arch_paths_exclude))
-				}
+	for axis, configToProps := range m.GetArchVariantProperties(ctx, &customProps{}) {
+		for config, props := range configToProps {
+			if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
+				paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrcExcludes(ctx, archProps.Arch_paths, archProps.Arch_paths_exclude))
 			}
 		}
-
-		paths.ResolveExcludes()
-
-		attrs := &customBazelModuleAttributes{
-			String_ptr_prop:  m.props.String_ptr_prop,
-			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",
-		}
-
-		ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
 	}
+
+	paths.ResolveExcludes()
+
+	attrs := &customBazelModuleAttributes{
+		String_ptr_prop:  m.props.String_ptr_prop,
+		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",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
 }
 
 // A bp2build mutator that uses load statements and creates a 1:M mapping from
 // module to target.
-func customBp2BuildMutatorFromStarlark(ctx android.TopDownMutatorContext) {
-	if m, ok := ctx.Module().(*customModule); ok {
-		if !m.ConvertWithBp2build(ctx) {
-			return
-		}
+func customBp2buildOneToMany(ctx android.TopDownMutatorContext, m *customModule) {
 
-		baseName := m.Name()
-		attrs := &customBazelModuleAttributes{}
+	baseName := m.Name()
+	attrs := &customBazelModuleAttributes{}
 
-		myLibraryProps := bazel.BazelTargetModuleProperties{
-			Rule_class:        "my_library",
-			Bzl_load_location: "//build/bazel/rules:rules.bzl",
-		}
-		ctx.CreateBazelTargetModule(myLibraryProps, android.CommonAttributes{Name: baseName}, attrs)
-
-		protoLibraryProps := bazel.BazelTargetModuleProperties{
-			Rule_class:        "proto_library",
-			Bzl_load_location: "//build/bazel/rules:proto.bzl",
-		}
-		ctx.CreateBazelTargetModule(protoLibraryProps, android.CommonAttributes{Name: baseName + "_proto_library_deps"}, attrs)
-
-		myProtoLibraryProps := bazel.BazelTargetModuleProperties{
-			Rule_class:        "my_proto_library",
-			Bzl_load_location: "//build/bazel/rules:proto.bzl",
-		}
-		ctx.CreateBazelTargetModule(myProtoLibraryProps, android.CommonAttributes{Name: baseName + "_my_proto_library_deps"}, attrs)
+	myLibraryProps := bazel.BazelTargetModuleProperties{
+		Rule_class:        "my_library",
+		Bzl_load_location: "//build/bazel/rules:rules.bzl",
 	}
+	ctx.CreateBazelTargetModule(myLibraryProps, android.CommonAttributes{Name: baseName}, attrs)
+
+	protoLibraryProps := bazel.BazelTargetModuleProperties{
+		Rule_class:        "proto_library",
+		Bzl_load_location: "//build/bazel/rules:proto.bzl",
+	}
+	ctx.CreateBazelTargetModule(protoLibraryProps, android.CommonAttributes{Name: baseName + "_proto_library_deps"}, attrs)
+
+	myProtoLibraryProps := bazel.BazelTargetModuleProperties{
+		Rule_class:        "my_proto_library",
+		Bzl_load_location: "//build/bazel/rules:proto.bzl",
+	}
+	ctx.CreateBazelTargetModule(myProtoLibraryProps, android.CommonAttributes{Name: baseName + "_my_proto_library_deps"}, attrs)
 }
 
 // Helper method for tests to easily access the targets in a dir.
@@ -353,7 +352,6 @@
 
 func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
 	ctx.RegisterModuleType("custom", customModuleFactory)
-	ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
 	ctx.RegisterForBazelConversion()
 }
 
diff --git a/bpf/bpf.go b/bpf/bpf.go
index fa1a84d..14b2d84 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -20,9 +20,9 @@
 	"strings"
 
 	"android/soong/android"
-	_ "android/soong/cc/config"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -41,6 +41,14 @@
 			CommandDeps: []string{"$ccCmd"},
 		},
 		"ccCmd", "cFlags")
+
+	stripRule = pctx.AndroidStaticRule("stripRule",
+		blueprint.RuleParams{
+			Command: `$stripCmd --strip-unneeded --remove-section=.rel.BTF ` +
+				`--remove-section=.rel.BTF.ext --remove-section=.BTF.ext $in -o $out`,
+			CommandDeps: []string{"$stripCmd"},
+		},
+		"stripCmd")
 )
 
 func registerBpfBuildComponents(ctx android.RegistrationContext) {
@@ -54,12 +62,21 @@
 	android.Module
 
 	OutputFiles(tag string) (android.Paths, error)
+
+	// Returns the sub install directory if the bpf module is included by apex.
+	SubDir() string
 }
 
 type BpfProperties struct {
 	Srcs         []string `android:"path"`
 	Cflags       []string
 	Include_dirs []string
+	Sub_dir      string
+	// If set to true, generate BTF debug info for maps & programs
+	Btf    *bool
+	Vendor *bool
+
+	VendorInternal bool `blueprint:"mutated"`
 }
 
 type bpf struct {
@@ -70,6 +87,41 @@
 	objs android.Paths
 }
 
+var _ android.ImageInterface = (*bpf)(nil)
+
+func (bpf *bpf) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+
+func (bpf *bpf) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return !proptools.Bool(bpf.properties.Vendor)
+}
+
+func (bpf *bpf) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (bpf *bpf) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (bpf *bpf) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (bpf *bpf) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+	return false
+}
+
+func (bpf *bpf) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+	if proptools.Bool(bpf.properties.Vendor) {
+		return []string{"vendor"}
+	}
+	return nil
+}
+
+func (bpf *bpf) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+	bpf.properties.VendorInternal = variation == "vendor"
+}
+
 func (bpf *bpf) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	cflags := []string{
 		"-nostdlibinc",
@@ -83,9 +135,9 @@
 		// The architecture doesn't matter here, but asm/types.h is included by linux/types.h.
 		"-isystem bionic/libc/kernel/uapi/asm-arm64",
 		"-isystem bionic/libc/kernel/android/uapi",
+		"-I       frameworks/libs/net/common/native/bpf_headers/include/bpf",
 		// TODO(b/149785767): only give access to specific file with AID_* constants
 		"-I       system/core/libcutils/include",
-		"-I       system/bpf/progs/include",
 		"-I " + ctx.ModuleDir(),
 	}
 
@@ -95,10 +147,14 @@
 
 	cflags = append(cflags, bpf.properties.Cflags...)
 
+	if proptools.Bool(bpf.properties.Btf) {
+		cflags = append(cflags, "-g")
+	}
+
 	srcs := android.PathsForModuleSrc(ctx, bpf.properties.Srcs)
 
 	for _, src := range srcs {
-		obj := android.ObjPathWithExt(ctx, "", src, "o")
+		obj := android.ObjPathWithExt(ctx, "unstripped", src, "o")
 
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   ccRule,
@@ -110,7 +166,21 @@
 			},
 		})
 
-		bpf.objs = append(bpf.objs, obj.WithoutRel())
+		if proptools.Bool(bpf.properties.Btf) {
+			objStripped := android.ObjPathWithExt(ctx, "", src, "o")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   stripRule,
+				Input:  obj,
+				Output: objStripped,
+				Args: map[string]string{
+					"stripCmd": "${config.ClangBin}/llvm-strip",
+				},
+			})
+			bpf.objs = append(bpf.objs, objStripped.WithoutRel())
+		} else {
+			bpf.objs = append(bpf.objs, obj.WithoutRel())
+		}
+
 	}
 }
 
@@ -121,6 +191,15 @@
 			fmt.Fprintln(w)
 			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
 			fmt.Fprintln(w)
+			var localModulePath string
+			if bpf.properties.VendorInternal {
+				localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_ETC)/bpf"
+			} else {
+				localModulePath = "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf"
+			}
+			if len(bpf.properties.Sub_dir) > 0 {
+				localModulePath += "/" + bpf.properties.Sub_dir
+			}
 			for _, obj := range bpf.objs {
 				objName := name + "_" + obj.Base()
 				names = append(names, objName)
@@ -130,7 +209,7 @@
 				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", obj.String())
 				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", obj.Base())
 				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC")
-				fmt.Fprintln(w, "LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/bpf")
+				fmt.Fprintln(w, localModulePath)
 				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
 				fmt.Fprintln(w)
 			}
@@ -154,6 +233,10 @@
 	}
 }
 
+func (bpf *bpf) SubDir() string {
+	return bpf.properties.Sub_dir
+}
+
 var _ android.OutputFileProducer = (*bpf)(nil)
 
 func BpfFactory() android.Module {
diff --git a/bpfix/Android.bp b/bpfix/Android.bp
index 345dbd0..a72d9b4 100644
--- a/bpfix/Android.bp
+++ b/bpfix/Android.bp
@@ -52,5 +52,6 @@
     ],
     deps: [
         "blueprint-parser",
+        "blueprint-pathtools",
     ],
 }
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index e1140b8..4f7d88c 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -22,6 +22,7 @@
 	"flag"
 	"fmt"
 	"io"
+	"io/ioutil"
 	"os"
 	"path/filepath"
 	"reflect"
@@ -29,6 +30,7 @@
 	"strings"
 
 	"github.com/google/blueprint/parser"
+	"github.com/google/blueprint/pathtools"
 )
 
 // Reformat takes a blueprint file as a string and returns a formatted version
@@ -156,6 +158,10 @@
 		Name: "formatFlagProperties",
 		Fix:  runPatchListMod(formatFlagProperties),
 	},
+	{
+		Name: "removeResourcesAndAssetsIfDefault",
+		Fix:  removeResourceAndAssetsIfDefault,
+	},
 }
 
 // for fix that only need to run once
@@ -166,7 +172,7 @@
 	},
 	{
 		Name: "rewriteLicenseProperties",
-		Fix:  runPatchListMod(rewriteLicenseProperties),
+		Fix:  runPatchListMod(rewriteLicenseProperty(nil, "")),
 	},
 }
 
@@ -608,7 +614,11 @@
 }
 
 var localModuleUpdate = map[string][]etcPrebuiltModuleUpdate{
-	"HOST_OUT":    {{prefix: "/etc", modType: "prebuilt_etc_host"}, {prefix: "/usr/share", modType: "prebuilt_usr_share_host"}},
+	"HOST_OUT": {
+		{prefix: "/etc", modType: "prebuilt_etc_host"},
+		{prefix: "/usr/share", modType: "prebuilt_usr_share_host"},
+		{prefix: "", modType: "prebuilt_root_host"},
+	},
 	"PRODUCT_OUT": {{prefix: "/system/etc"}, {prefix: "/vendor/etc", flags: []string{"proprietary"}}},
 	"TARGET_OUT": {{prefix: "/usr/share", modType: "prebuilt_usr_share"}, {prefix: "/fonts", modType: "prebuilt_font"},
 		{prefix: "/etc/firmware", modType: "prebuilt_firmware"}, {prefix: "/vendor/firmware", modType: "prebuilt_firmware", flags: []string{"proprietary"}},
@@ -639,7 +649,7 @@
 		// 'srcs' --> 'src' conversion
 		convertToSingleSource(mod, "src")
 
-		renameProperty(mod, "sub_dir", "relative_install_dir")
+		renameProperty(mod, "sub_dir", "relative_install_path")
 
 		// The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute
 		// 'local_module_path'. Analyze its contents and create the correct sub_dir:,
@@ -880,6 +890,24 @@
 	return nil
 }
 
+func removeResourceAndAssetsIfDefault(f *Fixer) error {
+	for _, def := range f.tree.Defs {
+		mod, ok := def.(*parser.Module)
+		if !ok {
+			continue
+		}
+		resourceDirList, resourceDirFound := getLiteralListPropertyValue(mod, "resource_dirs")
+		if resourceDirFound && len(resourceDirList) == 1 && resourceDirList[0] == "res" {
+			removeProperty(mod, "resource_dirs")
+		}
+		assetDirList, assetDirFound := getLiteralListPropertyValue(mod, "asset_dirs")
+		if assetDirFound && len(assetDirList) == 1 && assetDirList[0] == "assets" {
+			removeProperty(mod, "asset_dirs")
+		}
+	}
+	return nil
+}
+
 // Converts the default source list property, 'srcs', to a single source property with a given name.
 // "LOCAL_MODULE" reference is also resolved during the conversion process.
 func convertToSingleSource(mod *parser.Module, srcPropertyName string) {
@@ -1452,9 +1480,16 @@
 	return nil
 }
 
+func rewriteLicenseProperty(fs pathtools.FileSystem, relativePath string) patchListModFunction {
+	return func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
+		return rewriteLicenseProperties(mod, patchList, fs, relativePath)
+	}
+}
+
 // rewrite the "android_license_kinds" and "android_license_files" properties to a package module
 // (and a license module when needed).
-func rewriteLicenseProperties(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
+func rewriteLicenseProperties(mod *parser.Module, patchList *parser.PatchList, fs pathtools.FileSystem,
+	relativePath string) error {
 	// if a package module has been added, no more action is needed.
 	for _, patch := range *patchList {
 		if strings.Contains(patch.Replacement, "package {") {
@@ -1462,15 +1497,33 @@
 		}
 	}
 
+	// initial the fs
+	if fs == nil {
+		fs = pathtools.NewOsFs(os.Getenv("ANDROID_BUILD_TOP"))
+	}
+
+	// initial the relativePath
+	if len(relativePath) == 0 {
+		relativePath = getModuleRelativePath()
+	}
+	// validate the relativePath
+	ok := hasFile(relativePath+"/Android.mk", fs)
+	// some modules in the existing test cases in the androidmk_test.go do not have a valid path
+	if !ok && len(relativePath) > 0 {
+		return fmt.Errorf("Cannot find an Android.mk file at path %q", relativePath)
+	}
+
 	licenseKindsPropertyName := "android_license_kinds"
 	licenseFilesPropertyName := "android_license_files"
 
-	androidBpFileErr := "// Error: No Android.bp file is found at\n" +
+	androidBpFileErr := "// Error: No Android.bp file is found at path\n" +
 		"// %s\n" +
-		"// Please add one there with the needed license module first.\n"
+		"// Please add one there with the needed license module first.\n" +
+		"// Then reset the default_applicable_licenses property below with the license module name.\n"
 	licenseModuleErr := "// Error: Cannot get the name of the license module in the\n" +
 		"// %s file.\n" +
-		"// If no such license module exists, please add one there first.\n"
+		"// If no such license module exists, please add one there first.\n" +
+		"// Then reset the default_applicable_licenses property below with the license module name.\n"
 
 	defaultApplicableLicense := "Android-Apache-2.0"
 	var licenseModuleName, licensePatch string
@@ -1482,15 +1535,16 @@
 		// if have LOCAL_NOTICE_FILE outside the current directory, need to find and refer to the license
 		// module in the LOCAL_NOTICE_FILE location directly and no new license module needs to be created
 		if hasFileInParentDir {
-			bpPath, ok := getPathFromProperty(mod, licenseFilesPropertyName)
+			bpPath, ok := getPathFromProperty(mod, licenseFilesPropertyName, fs, relativePath)
 			if !ok {
-				bpDir, err := getDirFromProperty(mod, licenseFilesPropertyName)
+				bpDir, err := getDirFromProperty(mod, licenseFilesPropertyName, fs, relativePath)
 				if err != nil {
 					return err
 				}
 				licensePatch += fmt.Sprintf(androidBpFileErr, bpDir)
+				defaultApplicableLicense = ""
 			} else {
-				licenseModuleName, _ = getModuleName(bpPath, "license")
+				licenseModuleName, _ = getModuleName(bpPath, "license", fs)
 				if len(licenseModuleName) == 0 {
 					licensePatch += fmt.Sprintf(licenseModuleErr, bpPath)
 				}
@@ -1498,7 +1552,6 @@
 			}
 		} else {
 			// if have LOCAL_NOTICE_FILE in the current directory, need to create a new license module
-			relativePath := getModuleRelativePath()
 			if len(relativePath) == 0 {
 				return fmt.Errorf("Cannot obtain the relative path of the Android.mk file")
 			}
@@ -1617,17 +1670,14 @@
 	return absPath
 }
 
-// check whether a file exists in a directory
-func hasFile(dir string, fileName string) error {
-	_, err := os.Stat(dir + fileName)
-	if err != nil {
-		return err
-	}
-	return nil
+// check whether a file exists in a filesystem
+func hasFile(path string, fs pathtools.FileSystem) bool {
+	ok, _, _ := fs.Exists(path)
+	return ok
 }
 
 // get the directory where an `Android.bp` file and the property files are expected to locate
-func getDirFromProperty(mod *parser.Module, property string) (string, error) {
+func getDirFromProperty(mod *parser.Module, property string, fs pathtools.FileSystem, relativePath string) (string, error) {
 	listValue, ok := getLiteralListPropertyValue(mod, property)
 	if !ok {
 		// if do not find
@@ -1637,7 +1687,14 @@
 		// if empty
 		return "", fmt.Errorf("Cannot find the value of the %s.%s property", mod.Type, property)
 	}
-	path := getModuleAbsolutePath()
+	if relativePath == "" {
+		relativePath = "."
+	}
+	_, isDir, _ := fs.Exists(relativePath)
+	if !isDir {
+		return "", fmt.Errorf("Cannot find the path %q", relativePath)
+	}
+	path := relativePath
 	for {
 		if !strings.HasPrefix(listValue[0], "../") {
 			break
@@ -1645,25 +1702,29 @@
 		path = filepath.Dir(path)
 		listValue[0] = strings.TrimPrefix(listValue[0], "../")
 	}
+	_, isDir, _ = fs.Exists(path)
+	if !isDir {
+		return "", fmt.Errorf("Cannot find the path %q", path)
+	}
 	return path, nil
 }
 
 // get the path of the `Android.bp` file at the expected location where the property files locate
-func getPathFromProperty(mod *parser.Module, property string) (string, bool) {
-	dir, err := getDirFromProperty(mod, property)
+func getPathFromProperty(mod *parser.Module, property string, fs pathtools.FileSystem, relativePath string) (string, bool) {
+	dir, err := getDirFromProperty(mod, property, fs, relativePath)
 	if err != nil {
 		return "", false
 	}
-	err = hasFile(dir, "/Android.bp")
-	if err != nil {
+	ok := hasFile(dir+"/Android.bp", fs)
+	if !ok {
 		return "", false
 	}
 	return dir + "/Android.bp", true
 }
 
 // parse an Android.bp file to get the name of the first module with type of moduleType
-func getModuleName(path string, moduleType string) (string, error) {
-	tree, err := parserPath(path)
+func getModuleName(path string, moduleType string, fs pathtools.FileSystem) (string, error) {
+	tree, err := parserPath(path, fs)
 	if err != nil {
 		return "", err
 	}
@@ -1685,8 +1746,13 @@
 }
 
 // parse an Android.bp file with the specific path
-func parserPath(path string) (tree *parser.File, err error) {
-	fileContent, _ := os.ReadFile(path)
+func parserPath(path string, fs pathtools.FileSystem) (tree *parser.File, err error) {
+	f, err := fs.Open(path)
+	if err != nil {
+		return tree, err
+	}
+	defer f.Close()
+	fileContent, _ := ioutil.ReadAll(f)
 	tree, err = parse(path, bytes.NewBufferString(string(fileContent)))
 	if err != nil {
 		return tree, err
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index e6b6af5..17b3c24 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -19,12 +19,12 @@
 import (
 	"bytes"
 	"fmt"
+	"reflect"
 	"strings"
 	"testing"
 
-	"reflect"
-
 	"github.com/google/blueprint/parser"
+	"github.com/google/blueprint/pathtools"
 )
 
 // TODO(jeffrygaston) remove this when position is removed from ParseNode (in b/38325146) and we can directly do reflect.DeepEqual
@@ -823,7 +823,47 @@
 			out: `prebuilt_etc {
 			name: "foo",
 			src: "bar",
-			relative_install_dir: "baz",
+			relative_install_path: "baz",
+		}
+		`,
+		},
+		{
+			name: "prebuilt_etc sub_dir",
+			in: `
+			prebuilt_etc_host {
+			name: "foo",
+			src: "bar",
+			local_module_path: {
+				var: "HOST_OUT",
+				fixed: "/etc/baz",
+			},
+		}
+		`,
+			out: `prebuilt_etc_host {
+			name: "foo",
+			src: "bar",
+			relative_install_path: "baz",
+
+		}
+		`,
+		},
+		{
+			name: "prebuilt_etc sub_dir",
+			in: `
+			prebuilt_etc_host {
+			name: "foo",
+			src: "bar",
+			local_module_path: {
+				var: "HOST_OUT",
+				fixed: "/baz/sub",
+			},
+		}
+		`,
+			out: `prebuilt_root_host {
+			name: "foo",
+			src: "bar",
+			relative_install_path: "baz/sub",
+
 		}
 		`,
 		},
@@ -1678,10 +1718,20 @@
 	}
 }
 
-func TestRewriteLicenseProperties(t *testing.T) {
+func TestRewriteLicenseProperty(t *testing.T) {
+	mockFs := pathtools.MockFs(map[string][]byte{
+		"a/b/c/d/Android.mk": []byte("this is not important."),
+		"a/b/LicenseFile1":   []byte("LicenseFile1"),
+		"a/b/LicenseFile2":   []byte("LicenseFile2"),
+		"a/b/Android.bp":     []byte("license {\n\tname: \"reuse_a_b_license\",\n}\n"),
+	})
+	relativePath := "a/b/c/d"
+	relativePathErr := "a/b/c"
 	tests := []struct {
 		name string
 		in   string
+		fs   pathtools.FileSystem
+		path string
 		out  string
 	}{
 		{
@@ -1744,13 +1794,194 @@
 				}
 			`,
 		},
-		// TODO(b/205615944): When valid "android_license_files" exists, the test requires an Android.mk
-		// file (and an Android.bp file is required as well if the license files locates outside the current
-		// directory). So plan to use a mock file system to mock the Android.mk and Android.bp files.
+		{
+			name: "license rewriting with license files in the current directory",
+			in: `
+				android_test {
+					name: "foo",
+					android_license_kinds: ["license_kind"],
+					android_license_conditions: ["license_notice"],
+					android_license_files: ["LicenseFile1", "LicenseFile2",],
+				}
+			`,
+			fs:   mockFs,
+			path: relativePath,
+			out: `
+			package {
+				// See: http://go/android-license-faq
+				default_applicable_licenses: [
+					"a_b_c_d_license",
+				],
+			}
+
+			license {
+				name: "a_b_c_d_license",
+				visibility: [":__subpackages__"],
+				license_kinds: [
+					"license_kind",
+				],
+				license_text: [
+					"LicenseFile1",
+					"LicenseFile2",
+				],
+			}
+
+			android_test {
+				name: "foo",
+				android_license_kinds: ["license_kind"],
+				android_license_conditions: ["license_notice"],
+				android_license_files: [
+					"LicenseFile1",
+					"LicenseFile2",
+				],
+			}
+			`,
+		},
+		{
+			name: "license rewriting with license files outside the current directory",
+			in: `
+				android_test {
+					name: "foo",
+					android_license_kinds: ["license_kind"],
+					android_license_conditions: ["license_notice"],
+					android_license_files: ["../../LicenseFile1", "../../LicenseFile2",],
+				}
+			`,
+			fs:   mockFs,
+			path: relativePath,
+			out: `
+			package {
+				// See: http://go/android-license-faq
+				default_applicable_licenses: [
+					"reuse_a_b_license",
+				],
+			}
+
+			android_test {
+				name: "foo",
+				android_license_kinds: ["license_kind"],
+				android_license_conditions: ["license_notice"],
+				android_license_files: [
+					"../../LicenseFile1",
+					"../../LicenseFile2",
+				],
+			}
+			`,
+		},
+		{
+			name: "license rewriting with no Android.bp file in the expected location",
+			in: `
+				android_test {
+					name: "foo",
+					android_license_kinds: ["license_kind"],
+					android_license_conditions: ["license_notice"],
+					android_license_files: ["../../LicenseFile1", "../../LicenseFile2",],
+				}
+			`,
+			fs: pathtools.MockFs(map[string][]byte{
+				"a/b/c/d/Android.mk": []byte("this is not important."),
+				"a/b/LicenseFile1":   []byte("LicenseFile1"),
+				"a/b/LicenseFile2":   []byte("LicenseFile2"),
+				"a/Android.bp":       []byte("license {\n\tname: \"reuse_a_b_license\",\n}\n"),
+			}),
+			path: relativePath,
+			out: `
+			// Error: No Android.bp file is found at path
+			// a/b
+			// Please add one there with the needed license module first.
+			// Then reset the default_applicable_licenses property below with the license module name.
+			package {
+				// See: http://go/android-license-faq
+				default_applicable_licenses: [
+					"",
+				],
+			}
+
+			android_test {
+				name: "foo",
+				android_license_kinds: ["license_kind"],
+				android_license_conditions: ["license_notice"],
+				android_license_files: [
+					"../../LicenseFile1",
+					"../../LicenseFile2",
+				],
+			}
+			`,
+		},
+		{
+			name: "license rewriting with an Android.bp file without a license module",
+			in: `
+				android_test {
+					name: "foo",
+					android_license_kinds: ["license_kind"],
+					android_license_conditions: ["license_notice"],
+					android_license_files: ["../../LicenseFile1", "../../LicenseFile2",],
+				}
+			`,
+			fs: pathtools.MockFs(map[string][]byte{
+				"a/b/c/d/Android.mk": []byte("this is not important."),
+				"a/b/LicenseFile1":   []byte("LicenseFile1"),
+				"a/b/LicenseFile2":   []byte("LicenseFile2"),
+				"a/b/Android.bp":     []byte("non_license {\n\tname: \"reuse_a_b_license\",\n}\n"),
+			}),
+			path: relativePath,
+			out: `
+			// Error: Cannot get the name of the license module in the
+			// a/b/Android.bp file.
+			// If no such license module exists, please add one there first.
+			// Then reset the default_applicable_licenses property below with the license module name.
+			package {
+				// See: http://go/android-license-faq
+				default_applicable_licenses: [
+					"",
+				],
+			}
+
+			android_test {
+				name: "foo",
+				android_license_kinds: ["license_kind"],
+				android_license_conditions: ["license_notice"],
+				android_license_files: [
+					"../../LicenseFile1",
+					"../../LicenseFile2",
+				],
+			}
+			`,
+		},
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			runPassOnce(t, test.in, test.out, runPatchListMod(rewriteLicenseProperties))
+			runPassOnce(t, test.in, test.out, runPatchListMod(rewriteLicenseProperty(test.fs, test.path)))
+		})
+	}
+
+	testErrs := []struct {
+		name        string
+		in          string
+		fs          pathtools.FileSystem
+		path        string
+		expectedErr string
+	}{
+		{
+			name: "license rewriting with a wrong path",
+			in: `
+				android_test {
+					name: "foo",
+					android_license_kinds: ["license_kind"],
+					android_license_conditions: ["license_notice"],
+					android_license_files: ["../../LicenseFile1", "../../LicenseFile2",],
+				}
+			`,
+			fs:   mockFs,
+			path: relativePathErr,
+			expectedErr: `
+				Cannot find an Android.mk file at path "a/b/c"
+			`,
+		},
+	}
+	for _, test := range testErrs {
+		t.Run(test.name, func(t *testing.T) {
+			checkError(t, test.in, test.expectedErr, runPatchListMod(rewriteLicenseProperty(test.fs, test.path)))
 		})
 	}
 }
@@ -1831,3 +2062,124 @@
 		})
 	}
 }
+
+func TestRemoveResourceAndAssetsIfDefault(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "resource_dirs default",
+			in: `
+			android_app {
+				name: "foo",
+				resource_dirs: ["res"],
+			}
+			`,
+			out: `
+			android_app {
+				name: "foo",
+
+			}
+			`,
+		},
+		{
+			name: "resource_dirs not default",
+			in: `
+			android_app {
+				name: "foo",
+				resource_dirs: ["reso"],
+			}
+			`,
+			out: `
+			android_app {
+				name: "foo",
+				resource_dirs: ["reso"],
+			}
+			`,
+		},
+		{
+			name: "resource_dirs includes not default",
+			in: `
+			android_app {
+				name: "foo",
+				resource_dirs: ["res", "reso"],
+			}
+			`,
+			out: `
+			android_app {
+				name: "foo",
+				resource_dirs: ["res", "reso"],
+			}
+			`,
+		}, {
+			name: "asset_dirs default",
+			in: `
+			android_app {
+				name: "foo",
+				asset_dirs: ["assets"],
+			}
+			`,
+			out: `
+			android_app {
+				name: "foo",
+
+			}
+			`,
+		},
+		{
+			name: "asset_dirs not default",
+			in: `
+			android_app {
+				name: "foo",
+				asset_dirs: ["assety"],
+			}
+			`,
+			out: `
+			android_app {
+				name: "foo",
+				asset_dirs: ["assety"],
+			}
+			`,
+		},
+		{
+			name: "asset_dirs includes not default",
+			in: `
+			android_app {
+				name: "foo",
+				asset_dirs: ["assets", "assety"],
+			}
+			`,
+			out: `
+			android_app {
+				name: "foo",
+				asset_dirs: ["assets", "assety"],
+			}
+			`,
+		},
+		{
+			name: "resource_dirs and asset_dirs both default",
+			in: `
+			android_app {
+				name: "foo",
+				asset_dirs: ["assets"],
+				resource_dirs: ["res"],
+			}
+			`,
+			out: `
+			android_app {
+				name: "foo",
+
+			}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPassOnce(t, test.in, test.out, func(fixer *Fixer) error {
+				return removeResourceAndAssetsIfDefault(fixer)
+			})
+		})
+	}
+}
diff --git a/cc/Android.bp b/cc/Android.bp
index 07aa7cb..9103a48 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -19,10 +19,11 @@
         "soong-tradefed",
     ],
     srcs: [
+        "afdo.go",
         "androidmk.go",
         "api_level.go",
-        "builder.go",
         "bp2build.go",
+        "builder.go",
         "cc.go",
         "ccdeps.go",
         "check.go",
@@ -67,7 +68,6 @@
         "native_bridge_sdk_trait.go",
         "object.go",
         "test.go",
-        "toolchain_library.go",
 
         "ndk_abi.go",
         "ndk_headers.go",
@@ -88,6 +88,7 @@
         "stub_library.go",
     ],
     testSrcs: [
+        "afdo_test.go",
         "cc_test.go",
         "compiler_test.go",
         "gen_test.go",
diff --git a/cc/OWNERS b/cc/OWNERS
index 6d7c30a..a438b15 100644
--- a/cc/OWNERS
+++ b/cc/OWNERS
@@ -1,4 +1,4 @@
 per-file ndk_*.go = danalbert@google.com
 per-file tidy.go = srhines@google.com, chh@google.com
-per-file lto.go,pgo.go = srhines@google.com, pirama@google.com, yikong@google.com
-
+per-file afdo.go,afdo_test.go,lto.go,pgo.go = srhines@google.com, pirama@google.com, yikong@google.com
+per-file coverage.go = pirama@google.com, srhines@google.com, allenhair@google.com
diff --git a/cc/afdo.go b/cc/afdo.go
new file mode 100644
index 0000000..d7cce77
--- /dev/null
+++ b/cc/afdo.go
@@ -0,0 +1,200 @@
+// 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 cc
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+)
+
+var (
+	globalAfdoProfileProjects = []string{
+		"vendor/google_data/pgo_profile/sampling/",
+		"toolchain/pgo-profiles/sampling/",
+	}
+)
+
+var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects")
+
+const afdoCFlagsFormat = "-fprofile-sample-accurate -fprofile-sample-use=%s"
+
+func getAfdoProfileProjects(config android.DeviceConfig) []string {
+	return config.OnceStringSlice(afdoProfileProjectsConfigKey, func() []string {
+		return append(globalAfdoProfileProjects, config.AfdoAdditionalProfileDirs()...)
+	})
+}
+
+func recordMissingAfdoProfileFile(ctx android.BaseModuleContext, missing string) {
+	getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
+}
+
+type AfdoProperties struct {
+	Afdo bool
+
+	AfdoTarget *string  `blueprint:"mutated"`
+	AfdoDeps   []string `blueprint:"mutated"`
+}
+
+type afdo struct {
+	Properties AfdoProperties
+}
+
+func (afdo *afdo) props() []interface{} {
+	return []interface{}{&afdo.Properties}
+}
+
+func (afdo *afdo) AfdoEnabled() bool {
+	return afdo != nil && afdo.Properties.Afdo && afdo.Properties.AfdoTarget != nil
+}
+
+// Get list of profile file names, ordered by level of specialisation. For example:
+//   1. libfoo_arm64.afdo
+//   2. libfoo.afdo
+// Add more specialisation as needed.
+func getProfileFiles(ctx android.BaseModuleContext, moduleName string) []string {
+	var files []string
+	files = append(files, moduleName+"_"+ctx.Arch().ArchType.String()+".afdo")
+	files = append(files, moduleName+".afdo")
+	return files
+}
+
+func (props *AfdoProperties) GetAfdoProfileFile(ctx android.BaseModuleContext, module string) android.OptionalPath {
+	// Test if the profile_file is present in any of the Afdo profile projects
+	for _, profileFile := range getProfileFiles(ctx, module) {
+		for _, profileProject := range getAfdoProfileProjects(ctx.DeviceConfig()) {
+			path := android.ExistentPathForSource(ctx, profileProject, profileFile)
+			if path.Valid() {
+				return path
+			}
+		}
+	}
+
+	// Record that this module's profile file is absent
+	missing := ctx.ModuleDir() + ":" + module
+	recordMissingAfdoProfileFile(ctx, missing)
+
+	return android.OptionalPathForPath(nil)
+}
+
+func (afdo *afdo) begin(ctx BaseModuleContext) {
+	if ctx.Host() {
+		return
+	}
+	if ctx.static() && !ctx.staticBinary() {
+		return
+	}
+	if afdo.Properties.Afdo {
+		module := ctx.ModuleName()
+		if afdo.Properties.GetAfdoProfileFile(ctx, module).Valid() {
+			afdo.Properties.AfdoTarget = proptools.StringPtr(module)
+		}
+	}
+}
+
+func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags {
+	if profile := afdo.Properties.AfdoTarget; profile != nil {
+		if profileFile := afdo.Properties.GetAfdoProfileFile(ctx, *profile); profileFile.Valid() {
+			profileFilePath := profileFile.Path()
+
+			profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, profileFile)
+			flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlag)
+			flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlag)
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm,-no-warn-sample-unused=true")
+
+			// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
+			// if profileFile gets updated
+			flags.CFlagsDeps = append(flags.CFlagsDeps, profileFilePath)
+			flags.LdFlagsDeps = append(flags.LdFlagsDeps, profileFilePath)
+		}
+	}
+
+	return flags
+}
+
+// Propagate afdo requirements down from binaries
+func afdoDepsMutator(mctx android.TopDownMutatorContext) {
+	if m, ok := mctx.Module().(*Module); ok && m.afdo.AfdoEnabled() {
+		afdoTarget := *m.afdo.Properties.AfdoTarget
+		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
+			tag := mctx.OtherModuleDependencyTag(dep)
+			libTag, isLibTag := tag.(libraryDependencyTag)
+
+			// Do not recurse down non-static dependencies
+			if isLibTag {
+				if !libTag.static() {
+					return false
+				}
+			} else {
+				if tag != objDepTag && tag != reuseObjTag {
+					return false
+				}
+			}
+
+			if dep, ok := dep.(*Module); ok {
+				dep.afdo.Properties.AfdoDeps = append(dep.afdo.Properties.AfdoDeps, afdoTarget)
+			}
+
+			return true
+		})
+	}
+}
+
+// Create afdo variants for modules that need them
+func afdoMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(*Module); ok && m.afdo != nil {
+		if m.afdo.AfdoEnabled() && !m.static() {
+			afdoTarget := *m.afdo.Properties.AfdoTarget
+			mctx.SetDependencyVariation(encodeTarget(afdoTarget))
+		}
+
+		variationNames := []string{""}
+		afdoDeps := android.FirstUniqueStrings(m.afdo.Properties.AfdoDeps)
+		for _, dep := range afdoDeps {
+			variationNames = append(variationNames, encodeTarget(dep))
+		}
+		if len(variationNames) > 1 {
+			modules := mctx.CreateVariations(variationNames...)
+			for i, name := range variationNames {
+				if name == "" {
+					continue
+				}
+				variation := modules[i].(*Module)
+				variation.Properties.PreventInstall = true
+				variation.Properties.HideFromMake = true
+				variation.afdo.Properties.AfdoTarget = proptools.StringPtr(decodeTarget(name))
+			}
+		}
+	}
+}
+
+// Encode target name to variation name.
+func encodeTarget(target string) string {
+	if target == "" {
+		return ""
+	}
+	return "afdo-" + target
+}
+
+// Decode target name from variation name.
+func decodeTarget(variation string) string {
+	if variation == "" {
+		return ""
+	}
+	return strings.TrimPrefix(variation, "afdo-")
+}
diff --git a/cc/afdo_test.go b/cc/afdo_test.go
new file mode 100644
index 0000000..5515464
--- /dev/null
+++ b/cc/afdo_test.go
@@ -0,0 +1,70 @@
+// Copyright 2022 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 cc
+
+import (
+	"testing"
+
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+func TestAfdoDeps(t *testing.T) {
+	bp := `
+	cc_library {
+		name: "libTest",
+		srcs: ["foo.c"],
+		static_libs: ["libFoo"],
+		afdo: true,
+	}
+
+	cc_library {
+		name: "libFoo",
+		static_libs: ["libBar"],
+	}
+
+	cc_library {
+		name: "libBar",
+	}
+	`
+	prepareForAfdoTest := android.FixtureAddTextFile("toolchain/pgo-profiles/sampling/libTest.afdo", "TEST")
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		prepareForAfdoTest,
+	).RunTestWithBp(t, bp)
+
+	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared").Module()
+	libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest").Module()
+	libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest").Module()
+
+	hasDep := func(m android.Module, wantDep android.Module) bool {
+		var found bool
+		result.VisitDirectDeps(m, func(dep blueprint.Module) {
+			if dep == wantDep {
+				found = true
+			}
+		})
+		return found
+	}
+
+	if !hasDep(libTest, libFoo) {
+		t.Errorf("libTest missing dependency on afdo variant of libFoo")
+	}
+
+	if !hasDep(libFoo, libBar) {
+		t.Errorf("libTest missing dependency on afdo variant of libBar")
+	}
+}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 636bab8..9290272 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -15,6 +15,8 @@
 package cc
 
 import (
+	"github.com/google/blueprint/proptools"
+
 	"fmt"
 	"io"
 	"path/filepath"
@@ -106,6 +108,9 @@
 				if len(c.Properties.AndroidMkHeaderLibs) > 0 {
 					entries.AddStrings("LOCAL_HEADER_LIBRARIES", c.Properties.AndroidMkHeaderLibs...)
 				}
+				if len(c.Properties.AndroidMkRuntimeLibs) > 0 {
+					entries.AddStrings("LOCAL_RUNTIME_LIBRARIES", c.Properties.AndroidMkRuntimeLibs...)
+				}
 				entries.SetString("LOCAL_SOONG_LINK_TYPE", c.makeLinkType)
 				if c.UseVndk() {
 					entries.SetBool("LOCAL_USE_VNDK", true)
@@ -241,7 +246,7 @@
 		entries.Class = "SHARED_LIBRARIES"
 		entries.ExtraEntries = append(entries.ExtraEntries, func(_ android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 			entries.SetString("LOCAL_SOONG_TOC", library.toc().String())
-			if !library.buildStubs() {
+			if !library.buildStubs() && library.unstrippedOutputFile != nil {
 				entries.SetString("LOCAL_SOONG_UNSTRIPPED_BINARY", library.unstrippedOutputFile.String())
 			}
 			if len(library.Properties.Overrides) > 0 {
@@ -394,6 +399,9 @@
 		}
 
 		entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(test.Properties.Per_testcase_directory))
+		if len(test.Properties.Data_bins) > 0 {
+			entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
+		}
 	})
 
 	AndroidMkWriteTestData(test.data, entries)
@@ -439,21 +447,13 @@
 	ctx.subAndroidMk(entries, test.libraryDecorator)
 }
 
-func (library *toolchainLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.Class = "STATIC_LIBRARIES"
-	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		_, suffix, _ := android.SplitFileExt(entries.OutputFile.Path().Base())
-		entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
-	})
-}
-
 func (installer *baseInstaller) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	if installer.path == (android.InstallPath{}) {
 		return
 	}
 
 	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-		path, file := filepath.Split(installer.path.ToMakePath().String())
+		path, file := filepath.Split(installer.path.String())
 		stem, suffix, _ := android.SplitFileExt(file)
 		entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
 		entries.SetString("LOCAL_MODULE_PATH", path)
@@ -532,7 +532,13 @@
 		c.libraryDecorator.androidMkWriteExportedFlags(entries)
 
 		if c.shared() || c.static() {
-			path, file := filepath.Split(c.path.ToMakePath().String())
+			src := c.path.String()
+			// For static libraries which aren't installed, directly use Src to extract filename.
+			// This is safe: generated snapshot modules have a real path as Src, not a module
+			if c.static() {
+				src = proptools.String(c.properties.Src)
+			}
+			path, file := filepath.Split(src)
 			stem, suffix, ext := android.SplitFileExt(file)
 			entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
 			entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
diff --git a/cc/binary.go b/cc/binary.go
index a5afb07..6c7d581 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -18,6 +18,7 @@
 	"path/filepath"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 	"android/soong/bazel"
@@ -68,13 +69,14 @@
 
 // cc_binary produces a binary that is runnable on a device.
 func BinaryFactory() android.Module {
-	module, _ := NewBinary(android.HostAndDeviceSupported)
+	module, _ := newBinary(android.HostAndDeviceSupported, true)
+	module.bazelHandler = &ccBinaryBazelHandler{module: module}
 	return module.Init()
 }
 
 // cc_binary_host produces a binary that is runnable on a host.
 func BinaryHostFactory() android.Module {
-	module, _ := NewBinary(android.HostSupported)
+	module, _ := newBinary(android.HostSupported, true)
 	return module.Init()
 }
 
@@ -192,6 +194,10 @@
 // Individual module implementations which comprise a C++ binary should call this function,
 // set some fields on the result, and then call the Init function.
 func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
+	return newBinary(hod, true)
+}
+
+func newBinary(hod android.HostOrDeviceSupported, bazelable bool) (*Module, *binaryDecorator) {
 	module := newModule(hod, android.MultilibFirst)
 	binary := &binaryDecorator{
 		baseLinker:    NewBaseLinker(module.sanitize),
@@ -200,6 +206,7 @@
 	module.compiler = NewBaseCompiler()
 	module.linker = binary
 	module.installer = binary
+	module.bazelable = bazelable
 
 	// Allow module to be added as member of an sdk/module_exports.
 	module.sdkMemberTypes = []android.SdkMemberType{
@@ -344,6 +351,12 @@
 		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-dynamic-linker")
 	}
 
+	if ctx.Darwin() && deps.DarwinSecondArchOutput.Valid() {
+		fatOutputFile := outputFile
+		outputFile = android.PathForModuleOut(ctx, "pre-fat", fileName)
+		transformDarwinUniversalBinary(ctx, fatOutputFile, outputFile, deps.DarwinSecondArchOutput.Path())
+	}
+
 	builderFlags := flagsToBuilderFlags(flags)
 	stripFlags := flagsToStripFlags(flags)
 	if binary.stripper.NeedsStrip(ctx) {
@@ -388,7 +401,7 @@
 		}
 	}
 
-	var validations android.WritablePaths
+	var validations android.Paths
 
 	// Handle host bionic linker symbols.
 	if ctx.Os() == android.LinuxBionic && !binary.static() {
@@ -411,9 +424,10 @@
 		linkerDeps = append(linkerDeps, deps.EarlySharedLibsDeps...)
 		linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
 		linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
+		linkerDeps = append(linkerDeps, ndkSharedLibDeps(ctx)...)
 	}
 
-	validations = append(validations, objs.tidyFiles...)
+	validations = append(validations, objs.tidyDepFiles...)
 	linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
 
 	// Register link action.
@@ -543,44 +557,40 @@
 	})
 }
 
-func init() {
-	android.RegisterBp2BuildMutator("cc_binary", BinaryBp2build)
-	android.RegisterBp2BuildMutator("cc_binary_host", BinaryHostBp2build)
+type ccBinaryBazelHandler struct {
+	android.BazelHandler
+
+	module *Module
 }
 
-func BinaryBp2build(ctx android.TopDownMutatorContext) {
-	binaryBp2build(ctx, "cc_binary")
+func (handler *ccBinaryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+	bazelCtx := ctx.Config().BazelContext
+	filePaths, ok := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
+	if ok {
+		if len(filePaths) != 1 {
+			ctx.ModuleErrorf("expected exactly one output file for '%s', but got %s", label, filePaths)
+			return false
+		}
+		outputFilePath := android.PathForBazelOut(ctx, filePaths[0])
+		handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+		// TODO(b/220164721): We need to decide if we should return the stripped as the unstripped.
+		handler.module.linker.(*binaryDecorator).unstrippedOutputFile = outputFilePath
+	}
+	return ok
 }
 
-func BinaryHostBp2build(ctx android.TopDownMutatorContext) {
-	binaryBp2build(ctx, "cc_binary_host")
-}
-
-func binaryBp2build(ctx android.TopDownMutatorContext, typ string) {
-	m, ok := ctx.Module().(*Module)
-	if !ok {
-		// Not a cc module
-		return
-	}
-	if !m.ConvertWithBp2build(ctx) {
-		return
-	}
-
-	if ctx.ModuleType() != typ {
-		return
-	}
-
-	var compatibleWith bazel.StringListAttribute
-	if typ == "cc_binary_host" {
-		//incompatible with android OS
-		compatibleWith.SetSelectValue(bazel.OsConfigurationAxis, android.Android.Name, []string{"@platforms//:incompatible"})
-		compatibleWith.SetSelectValue(bazel.OsConfigurationAxis, bazel.ConditionsDefaultConfigKey, []string{})
-	}
-
+func binaryBp2build(ctx android.TopDownMutatorContext, m *Module, typ string) {
 	baseAttrs := bp2BuildParseBaseProps(ctx, m)
+	binaryLinkerAttrs := bp2buildBinaryLinkerProps(ctx, m)
+
+	if proptools.BoolDefault(binaryLinkerAttrs.Linkshared, true) {
+		baseAttrs.implementationDynamicDeps.Add(baseAttrs.protoDependency)
+	} else {
+		baseAttrs.implementationDeps.Add(baseAttrs.protoDependency)
+	}
 
 	attrs := &binaryAttributes{
-		binaryLinkerAttrs: bp2buildBinaryLinkerProps(ctx, m),
+		binaryLinkerAttrs: binaryLinkerAttrs,
 
 		Srcs:    baseAttrs.srcs,
 		Srcs_c:  baseAttrs.cSrcs,
@@ -615,13 +625,12 @@
 			None:                         baseAttrs.stripNone,
 		},
 
-		Target_compatible_with: compatibleWith,
-		Features:               baseAttrs.features,
+		Features: baseAttrs.features,
 	}
 
 	ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_binary",
-		Bzl_load_location: "//build/bazel/rules:cc_binary.bzl",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_binary.bzl",
 	},
 		android.CommonAttributes{Name: m.Name()},
 		attrs)
@@ -660,6 +669,4 @@
 	Strip stripAttributes
 
 	Features bazel.StringListAttribute
-
-	Target_compatible_with bazel.StringListAttribute
 }
diff --git a/cc/binary_test.go b/cc/binary_test.go
new file mode 100644
index 0000000..8ec3871
--- /dev/null
+++ b/cc/binary_test.go
@@ -0,0 +1,51 @@
+// Copyright 2022 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 cc
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestCcBinaryWithBazel(t *testing.T) {
+	bp := `
+cc_binary {
+	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",
+		LabelToOutputFiles: map[string][]string{
+			"//foo/bar:bar": []string{"foo"},
+		},
+	}
+	ctx := testCcWithConfig(t, config)
+
+	binMod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module()
+	producer := binMod.(android.OutputFileProducer)
+	outputFiles, err := producer.OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_binary outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{"outputbase/execroot/__main__/foo"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+	unStrippedFilePath := binMod.(*Module).UnstrippedOutputFile()
+	expectedUnStrippedFile := "outputbase/execroot/__main__/foo"
+	android.AssertStringEquals(t, "Unstripped output file", expectedUnStrippedFile, unStrippedFilePath.String())
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 888c3ba..42fc0e4 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -16,6 +16,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"regexp"
 	"strings"
 
 	"android/soong/android"
@@ -27,9 +28,16 @@
 )
 
 const (
-	cSrcPartition   = "c"
-	asSrcPartition  = "as"
-	cppSrcPartition = "cpp"
+	cSrcPartition     = "c"
+	asSrcPartition    = "as"
+	cppSrcPartition   = "cpp"
+	protoSrcPartition = "proto"
+)
+
+var (
+	// ignoring case, checks for proto or protos as an independent word in the name, whether at the
+	// beginning, end, or middle. e.g. "proto.foo", "bar-protos", "baz_proto_srcs" would all match
+	filegroupLikelyProtoPattern = regexp.MustCompile("(?i)(^|[^a-z])proto(s)?([^a-z]|$)")
 )
 
 // staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
@@ -41,49 +49,52 @@
 	Hdrs    bazel.LabelListAttribute
 	Copts   bazel.StringListAttribute
 
-	Deps                        bazel.LabelListAttribute
-	Implementation_deps         bazel.LabelListAttribute
-	Dynamic_deps                bazel.LabelListAttribute
-	Implementation_dynamic_deps bazel.LabelListAttribute
-	Whole_archive_deps          bazel.LabelListAttribute
+	Deps                              bazel.LabelListAttribute
+	Implementation_deps               bazel.LabelListAttribute
+	Dynamic_deps                      bazel.LabelListAttribute
+	Implementation_dynamic_deps       bazel.LabelListAttribute
+	Whole_archive_deps                bazel.LabelListAttribute
+	Implementation_whole_archive_deps bazel.LabelListAttribute
 
 	System_dynamic_deps bazel.LabelListAttribute
+
+	Enabled bazel.BoolAttribute
 }
 
 func groupSrcsByExtension(ctx android.BazelConversionPathContext, srcs bazel.LabelListAttribute) bazel.PartitionToLabelListAttribute {
-	// Check that a module is a filegroup type named <label>.
-	isFilegroupNamed := func(m android.Module, fullLabel string) bool {
-		if ctx.OtherModuleType(m) != "filegroup" {
-			return false
-		}
-		labelParts := strings.Split(fullLabel, ":")
-		if len(labelParts) > 2 {
-			// There should not be more than one colon in a label.
-			ctx.ModuleErrorf("%s is not a valid Bazel label for a filegroup", fullLabel)
-		}
-		return m.Name() == labelParts[len(labelParts)-1]
+	// Check that a module is a filegroup type
+	isFilegroup := func(m blueprint.Module) bool {
+		return ctx.OtherModuleType(m) == "filegroup"
 	}
 
 	// Convert filegroup dependencies into extension-specific filegroups filtered in the filegroup.bzl
 	// macro.
 	addSuffixForFilegroup := func(suffix string) bazel.LabelMapper {
-		return func(ctx bazel.OtherModuleContext, label string) (string, bool) {
-			m, exists := ctx.ModuleFromName(label)
-			if !exists {
-				return label, false
+		return func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+			m, exists := ctx.ModuleFromName(label.OriginalModuleName)
+			labelStr := label.Label
+			if !exists || !isFilegroup(m) {
+				return labelStr, false
 			}
-			aModule, _ := m.(android.Module)
-			if !isFilegroupNamed(aModule, label) {
-				return label, false
-			}
-			return label + suffix, true
+			return labelStr + suffix, true
 		}
 	}
 
+	isProtoFilegroup := func(ctx bazel.OtherModuleContext, label bazel.Label) (string, bool) {
+		m, exists := ctx.ModuleFromName(label.OriginalModuleName)
+		labelStr := label.Label
+		if !exists || !isFilegroup(m) {
+			return labelStr, false
+		}
+		likelyProtos := filegroupLikelyProtoPattern.MatchString(label.OriginalModuleName)
+		return labelStr, likelyProtos
+	}
+
 	// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
 	partitioned := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
-		cSrcPartition:  bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
-		asSrcPartition: bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
+		protoSrcPartition: bazel.LabelPartition{Extensions: []string{".proto"}, LabelMapper: isProtoFilegroup},
+		cSrcPartition:     bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
+		asSrcPartition:    bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
 		// C++ is the "catch-all" group, and comprises generated sources because we don't
 		// know the language of these sources until the genrule is executed.
 		cppSrcPartition: bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
@@ -153,7 +164,7 @@
 	attrs := staticOrSharedAttributes{}
 
 	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
-		attrs.Copts.SetSelectValue(axis, config, props.Cflags)
+		attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag))
 		attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
 		attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs))
 
@@ -166,6 +177,7 @@
 		attrs.Implementation_dynamic_deps.SetSelectValue(axis, config, sharedDeps.implementation)
 
 		attrs.Whole_archive_deps.SetSelectValue(axis, config, bazelLabelForWholeDeps(ctx, props.Whole_static_libs))
+		attrs.Enabled.SetSelectValue(axis, config, props.Enabled)
 	}
 	// system_dynamic_deps distinguishes between nil/empty list behavior:
 	//    nil -> use default values
@@ -195,6 +207,11 @@
 	attrs.Srcs_c = partitionedSrcs[cSrcPartition]
 	attrs.Srcs_as = partitionedSrcs[asSrcPartition]
 
+	if !partitionedSrcs[protoSrcPartition].IsEmpty() {
+		// TODO(b/208815215): determine whether this is used and add support if necessary
+		ctx.ModuleErrorf("Migrating static/shared only proto srcs is not currently supported")
+	}
+
 	return attrs
 }
 
@@ -230,6 +247,8 @@
 type baseAttributes struct {
 	compilerAttributes
 	linkerAttributes
+
+	protoDependency *bazel.LabelAttribute
 }
 
 // Convenience struct to hold all attributes parsed from compiler properties.
@@ -257,11 +276,27 @@
 
 	localIncludes    bazel.StringListAttribute
 	absoluteIncludes bazel.StringListAttribute
+
+	includes BazelIncludes
+
+	protoSrcs bazel.LabelListAttribute
+
+	stubsSymbolFile *string
+	stubsVersions   bazel.StringListAttribute
 }
 
-func parseCommandLineFlags(soongFlags []string) []string {
+type filterOutFn func(string) bool
+
+func filterOutStdFlag(flag string) bool {
+	return strings.HasPrefix(flag, "-std=")
+}
+
+func parseCommandLineFlags(soongFlags []string, filterOut filterOutFn) []string {
 	var result []string
 	for _, flag := range soongFlags {
+		if filterOut != nil && filterOut(flag) {
+			continue
+		}
 		// Soong's cflags can contain spaces, like `-include header.h`. For
 		// Bazel's copts, split them up to be compatible with the
 		// no_copts_tokenization feature.
@@ -288,10 +323,14 @@
 	ca.absoluteIncludes.SetSelectValue(axis, config, props.Include_dirs)
 	ca.localIncludes.SetSelectValue(axis, config, localIncludeDirs)
 
-	ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags))
-	ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags))
-	ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags))
-	ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags))
+	// In Soong, cflags occur on the command line before -std=<val> flag, resulting in the value being
+	// overridden. In Bazel we always allow overriding, via flags; however, this can cause
+	// incompatibilities, so we remove "-std=" flags from Cflag properties while leaving it in other
+	// cases.
+	ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag))
+	ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags, nil))
+	ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags, nil))
+	ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags, nil))
 	ca.rtti.SetSelectValue(axis, config, props.Rtti)
 }
 
@@ -337,6 +376,8 @@
 	ca.srcs.ResolveExcludes()
 	partitionedSrcs := groupSrcsByExtension(ctx, ca.srcs)
 
+	ca.protoSrcs = partitionedSrcs[protoSrcPartition]
+
 	for p, lla := range partitionedSrcs {
 		// if there are no sources, there is no need for headers
 		if lla.IsEmpty() {
@@ -399,10 +440,38 @@
 	return c_std_prop, cpp_std_prop
 }
 
-// bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
-func bp2BuildParseBaseProps(ctx android.BazelConversionPathContext, module *Module) baseAttributes {
+// packageFromLabel extracts package from a fully-qualified or relative Label and whether the label
+// is fully-qualified.
+// e.g. fully-qualified "//a/b:foo" -> "a/b", true, relative: ":bar" -> ".", false
+func packageFromLabel(label string) (string, bool) {
+	split := strings.Split(label, ":")
+	if len(split) != 2 {
+		return "", false
+	}
+	if split[0] == "" {
+		return ".", false
+	}
+	// remove leading "//"
+	return split[0][2:], true
+}
+
+// includesFromLabelList extracts relative/absolute includes from a bazel.LabelList>
+func includesFromLabelList(labelList bazel.LabelList) (relative, absolute []string) {
+	for _, hdr := range labelList.Includes {
+		if pkg, hasPkg := packageFromLabel(hdr.Label); hasPkg {
+			absolute = append(absolute, pkg)
+		} else if pkg != "" {
+			relative = append(relative, pkg)
+		}
+	}
+	return relative, absolute
+}
+
+// bp2BuildParseBaseProps returns all compiler, linker, library attributes of a cc module..
+func bp2BuildParseBaseProps(ctx android.Bp2buildMutatorContext, module *Module) baseAttributes {
 	archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
 	archVariantLinkerProps := module.GetArchVariantProperties(ctx, &BaseLinkerProperties{})
+	archVariantLibraryProperties := module.GetArchVariantProperties(ctx, &LibraryProperties{})
 
 	var implementationHdrs bazel.LabelListAttribute
 
@@ -419,6 +488,7 @@
 	}
 	allAxesAndConfigs(archVariantCompilerProps)
 	allAxesAndConfigs(archVariantLinkerProps)
+	allAxesAndConfigs(archVariantLibraryProperties)
 
 	compilerAttrs := compilerAttributes{}
 	linkerAttrs := linkerAttributes{}
@@ -442,6 +512,25 @@
 			headers := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrs, exportHdrs, android.BazelLabelForModuleDeps)
 			implementationHdrs.SetSelectValue(axis, config, headers.implementation)
 			compilerAttrs.hdrs.SetSelectValue(axis, config, headers.export)
+
+			exportIncludes, exportAbsoluteIncludes := includesFromLabelList(headers.export)
+			compilerAttrs.includes.Includes.SetSelectValue(axis, config, exportIncludes)
+			compilerAttrs.includes.AbsoluteIncludes.SetSelectValue(axis, config, exportAbsoluteIncludes)
+
+			includes, absoluteIncludes := includesFromLabelList(headers.implementation)
+			currAbsoluteIncludes := compilerAttrs.absoluteIncludes.SelectValue(axis, config)
+			currAbsoluteIncludes = android.FirstUniqueStrings(append(currAbsoluteIncludes, absoluteIncludes...))
+			compilerAttrs.absoluteIncludes.SetSelectValue(axis, config, currAbsoluteIncludes)
+			currIncludes := compilerAttrs.localIncludes.SelectValue(axis, config)
+			currIncludes = android.FirstUniqueStrings(append(currIncludes, includes...))
+			compilerAttrs.localIncludes.SetSelectValue(axis, config, currIncludes)
+
+			if libraryProps, ok := archVariantLibraryProperties[axis][config].(*LibraryProperties); ok {
+				if axis == bazel.NoConfigAxis {
+					compilerAttrs.stubsSymbolFile = libraryProps.Stubs.Symbol_file
+					compilerAttrs.stubsVersions.SetSelectValue(axis, config, libraryProps.Stubs.Versions)
+				}
+			}
 		}
 	}
 
@@ -454,25 +543,37 @@
 	(&linkerAttrs).convertProductVariables(ctx, productVariableProps)
 
 	(&compilerAttrs).finalize(ctx, implementationHdrs)
-	(&linkerAttrs).finalize()
+	(&linkerAttrs).finalize(ctx)
+
+	protoDep := bp2buildProto(ctx, module, compilerAttrs.protoSrcs)
+
+	// bp2buildProto will only set wholeStaticLib or implementationWholeStaticLib, but we don't know
+	// which. This will add the newly generated proto library to the appropriate attribute and nothing
+	// to the other
+	(&linkerAttrs).wholeArchiveDeps.Add(protoDep.wholeStaticLib)
+	(&linkerAttrs).implementationWholeArchiveDeps.Add(protoDep.implementationWholeStaticLib)
 
 	return baseAttributes{
 		compilerAttrs,
 		linkerAttrs,
+		protoDep.protoDep,
 	}
 }
 
 // Convenience struct to hold all attributes parsed from linker properties.
 type linkerAttributes struct {
-	deps                      bazel.LabelListAttribute
-	implementationDeps        bazel.LabelListAttribute
-	dynamicDeps               bazel.LabelListAttribute
-	implementationDynamicDeps bazel.LabelListAttribute
-	wholeArchiveDeps          bazel.LabelListAttribute
-	systemDynamicDeps         bazel.LabelListAttribute
+	deps                             bazel.LabelListAttribute
+	implementationDeps               bazel.LabelListAttribute
+	dynamicDeps                      bazel.LabelListAttribute
+	implementationDynamicDeps        bazel.LabelListAttribute
+	wholeArchiveDeps                 bazel.LabelListAttribute
+	implementationWholeArchiveDeps   bazel.LabelListAttribute
+	systemDynamicDeps                bazel.LabelListAttribute
+	usedSystemDynamicDepAsDynamicDep map[string]bool
 
 	linkCrt                       bazel.BoolAttribute
 	useLibcrt                     bazel.BoolAttribute
+	useVersionLib                 bazel.BoolAttribute
 	linkopts                      bazel.StringListAttribute
 	additionalLinkerInputs        bazel.LabelListAttribute
 	stripKeepSymbols              bazel.BoolAttribute
@@ -483,6 +584,10 @@
 	features                      bazel.StringListAttribute
 }
 
+var (
+	soongSystemSharedLibs = []string{"libc", "libm", "libdl"}
+)
+
 func (la *linkerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, isBinary bool, axis bazel.ConfigurationAxis, config string, props *BaseLinkerProperties) {
 	// Use a single variable to capture usage of nocrt in arch variants, so there's only 1 error message for this module
 	var axisFeatures []string
@@ -514,6 +619,17 @@
 	la.systemDynamicDeps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, systemSharedLibs))
 
 	sharedLibs := android.FirstUniqueStrings(props.Shared_libs)
+	excludeSharedLibs := props.Exclude_shared_libs
+	usedSystem := android.FilterListPred(sharedLibs, func(s string) bool {
+		return android.InList(s, soongSystemSharedLibs) && !android.InList(s, excludeSharedLibs)
+	})
+	for _, el := range usedSystem {
+		if la.usedSystemDynamicDepAsDynamicDep == nil {
+			la.usedSystemDynamicDepAsDynamicDep = map[string]bool{}
+		}
+		la.usedSystemDynamicDepAsDynamicDep[el] = true
+	}
+
 	sharedDeps := maybePartitionExportedAndImplementationsDepsExcludes(ctx, !isBinary, sharedLibs, props.Exclude_shared_libs, props.Export_shared_lib_headers, bazelLabelForSharedDepsExcludes)
 	la.dynamicDeps.SetSelectValue(axis, config, sharedDeps.export)
 	la.implementationDynamicDeps.SetSelectValue(axis, config, sharedDeps.implementation)
@@ -528,7 +644,7 @@
 
 	var linkerFlags []string
 	if len(props.Ldflags) > 0 {
-		linkerFlags = append(linkerFlags, props.Ldflags...)
+		linkerFlags = append(linkerFlags, proptools.NinjaEscapeList(props.Ldflags)...)
 		// binaries remove static flag if -shared is in the linker flags
 		if isBinary && android.InList("-shared", linkerFlags) {
 			axisFeatures = append(axisFeatures, "-static_flag")
@@ -542,6 +658,10 @@
 	la.linkopts.SetSelectValue(axis, config, linkerFlags)
 	la.useLibcrt.SetSelectValue(axis, config, props.libCrt())
 
+	if axis == bazel.NoConfigAxis {
+		la.useVersionLib.SetSelectValue(axis, config, props.Use_version_lib)
+	}
+
 	// it's very unlikely for nocrt to be arch variant, so bp2build doesn't support it.
 	if props.crt() != nil {
 		if axis == bazel.NoConfigAxis {
@@ -629,13 +749,25 @@
 	}
 }
 
-func (la *linkerAttributes) finalize() {
+func (la *linkerAttributes) finalize(ctx android.BazelConversionPathContext) {
+	// if system dynamic deps have the default value, any use of a system dynamic library used will
+	// result in duplicate library errors for bionic OSes. Here, we explicitly exclude those libraries
+	// from bionic OSes.
+	if la.systemDynamicDeps.IsNil() && len(la.usedSystemDynamicDepAsDynamicDep) > 0 {
+		toRemove := bazelLabelForSharedDeps(ctx, android.SortedStringKeys(la.usedSystemDynamicDepAsDynamicDep))
+		la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
+		la.dynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
+		la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "android", toRemove)
+		la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
+	}
+
 	la.deps.ResolveExcludes()
 	la.implementationDeps.ResolveExcludes()
 	la.dynamicDeps.ResolveExcludes()
 	la.implementationDynamicDeps.ResolveExcludes()
 	la.wholeArchiveDeps.ResolveExcludes()
 	la.systemDynamicDeps.ForceSpecifyEmptyList = true
+
 }
 
 // Relativize a list of root-relative paths with respect to the module's
@@ -664,13 +796,14 @@
 // BazelIncludes contains information about -I and -isystem paths from a module converted to Bazel
 // attributes.
 type BazelIncludes struct {
-	Includes       bazel.StringListAttribute
-	SystemIncludes bazel.StringListAttribute
+	AbsoluteIncludes bazel.StringListAttribute
+	Includes         bazel.StringListAttribute
+	SystemIncludes   bazel.StringListAttribute
 }
 
-func bp2BuildParseExportedIncludes(ctx android.BazelConversionPathContext, module *Module) BazelIncludes {
+func bp2BuildParseExportedIncludes(ctx android.BazelConversionPathContext, module *Module, existingIncludes BazelIncludes) BazelIncludes {
 	libraryDecorator := module.linker.(*libraryDecorator)
-	return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator)
+	return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator, &existingIncludes)
 }
 
 // Bp2buildParseExportedIncludesForPrebuiltLibrary returns a BazelIncludes with Bazel-ified values
@@ -678,25 +811,31 @@
 func Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx android.BazelConversionPathContext, module *Module) BazelIncludes {
 	prebuiltLibraryLinker := module.linker.(*prebuiltLibraryLinker)
 	libraryDecorator := prebuiltLibraryLinker.libraryDecorator
-	return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator)
+	return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator, nil)
 }
 
 // bp2BuildParseExportedIncludes creates a string list attribute contains the
 // exported included directories of a module.
-func bp2BuildParseExportedIncludesHelper(ctx android.BazelConversionPathContext, module *Module, libraryDecorator *libraryDecorator) BazelIncludes {
-	exported := BazelIncludes{}
+func bp2BuildParseExportedIncludesHelper(ctx android.BazelConversionPathContext, module *Module, libraryDecorator *libraryDecorator, includes *BazelIncludes) BazelIncludes {
+	var exported BazelIncludes
+	if includes != nil {
+		exported = *includes
+	} else {
+		exported = BazelIncludes{}
+	}
 	for axis, configToProps := range module.GetArchVariantProperties(ctx, &FlagExporterProperties{}) {
 		for config, props := range configToProps {
 			if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
 				if len(flagExporterProperties.Export_include_dirs) > 0 {
-					exported.Includes.SetSelectValue(axis, config, flagExporterProperties.Export_include_dirs)
+					exported.Includes.SetSelectValue(axis, config, android.FirstUniqueStrings(append(exported.Includes.SelectValue(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.SystemIncludes.SetSelectValue(axis, config, android.FirstUniqueStrings(append(exported.SystemIncludes.SelectValue(axis, config), flagExporterProperties.Export_system_include_dirs...)))
 				}
 			}
 		}
 	}
+	exported.AbsoluteIncludes.DeduplicateAxesFromBase()
 	exported.Includes.DeduplicateAxesFromBase()
 	exported.SystemIncludes.DeduplicateAxesFromBase()
 
@@ -705,10 +844,8 @@
 
 func bazelLabelForStaticModule(ctx android.BazelConversionPathContext, 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"
-		}
+	if ccModule, ok := m.(*Module); ok && ccModule.typ() == fullLibrary && !android.GenerateCcLibraryStaticOnly(m.Name()) {
+		label += "_bp2build_cc_library_static"
 	}
 	return label
 }
diff --git a/cc/builder.go b/cc/builder.go
index 72c2fa5..525b1a1 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -165,6 +165,12 @@
 		}
 	}()
 
+	darwinLipo = pctx.AndroidStaticRule("darwinLipo",
+		blueprint.RuleParams{
+			Command:     "${config.MacLipoPath} -create -output $out $in",
+			CommandDeps: []string{"${config.MacLipoPath}"},
+		})
+
 	_ = pctx.SourcePathVariable("archiveRepackPath", "build/soong/scripts/archive_repack.sh")
 
 	// Rule to repack an archive (.a) file with a subset of object files.
@@ -197,25 +203,34 @@
 		"clangBin", "format")
 
 	// Rule for invoking clang-tidy (a clang-based linter).
+	clangTidyDep, clangTidyDepRE = pctx.RemoteStaticRules("clangTidyDep",
+		blueprint.RuleParams{
+			Depfile: "$out",
+			Deps:    blueprint.DepsGCC,
+			Command: "${config.CcWrapper}$ccCmd $cFlags -E -o /dev/null $in " +
+				"-MQ $tidyFile -MD -MF $out",
+			CommandDeps: []string{"$ccCmd"},
+		},
+		&remoteexec.REParams{
+			Labels:       map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
+			ExecStrategy: "${config.REClangTidyExecStrategy}",
+			Inputs:       []string{"$in"},
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
+		}, []string{"ccCmd", "cFlags", "tidyFile"}, []string{})
+
 	clangTidy, clangTidyRE = pctx.RemoteStaticRules("clangTidy",
 		blueprint.RuleParams{
 			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"},
+			Command: "cp ${out}.dep ${out}.d && " +
+				"$tidyVars$reTemplate${config.ClangBin}/clang-tidy $tidyFlags $in -- $cFlags && " +
+				"touch $out",
+			CommandDeps: []string{"${config.ClangBin}/clang-tidy"},
 		},
 		&remoteexec.REParams{
 			Labels:               map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
 			ExecStrategy:         "${config.REClangTidyExecStrategy}",
-			Inputs:               []string{"$in"},
+			Inputs:               []string{"$in", "${out}.dep"},
 			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.
@@ -224,7 +239,7 @@
 			// (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{})
+		}, []string{"cFlags", "tidyFlags", "tidyVars"}, []string{})
 
 	_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
 
@@ -381,10 +396,11 @@
 	toolchain     config.Toolchain
 
 	// True if these extra features are enabled.
-	tidy         bool
-	gcovCoverage bool
-	sAbiDump     bool
-	emitXrefs    bool
+	tidy          bool
+	needTidyFiles bool
+	gcovCoverage  bool
+	sAbiDump      bool
+	emitXrefs     bool
 
 	assemblerWithCpp bool // True if .s files should be processed with the c preprocessor.
 
@@ -413,7 +429,8 @@
 // Objects is a collection of file paths corresponding to outputs for C++ related build statements.
 type Objects struct {
 	objFiles      android.Paths
-	tidyFiles     android.WritablePaths
+	tidyFiles     android.Paths
+	tidyDepFiles  android.Paths // link dependent .tidy files
 	coverageFiles android.Paths
 	sAbiDumpFiles android.Paths
 	kytheFiles    android.Paths
@@ -422,7 +439,8 @@
 func (a Objects) Copy() Objects {
 	return Objects{
 		objFiles:      append(android.Paths{}, a.objFiles...),
-		tidyFiles:     append(android.WritablePaths{}, a.tidyFiles...),
+		tidyFiles:     append(android.Paths{}, a.tidyFiles...),
+		tidyDepFiles:  append(android.Paths{}, a.tidyDepFiles...),
 		coverageFiles: append(android.Paths{}, a.coverageFiles...),
 		sAbiDumpFiles: append(android.Paths{}, a.sAbiDumpFiles...),
 		kytheFiles:    append(android.Paths{}, a.kytheFiles...),
@@ -433,35 +451,33 @@
 	return Objects{
 		objFiles:      append(a.objFiles, b.objFiles...),
 		tidyFiles:     append(a.tidyFiles, b.tidyFiles...),
+		tidyDepFiles:  append(a.tidyDepFiles, b.tidyDepFiles...),
 		coverageFiles: append(a.coverageFiles, b.coverageFiles...),
 		sAbiDumpFiles: append(a.sAbiDumpFiles, b.sAbiDumpFiles...),
 		kytheFiles:    append(a.kytheFiles, b.kytheFiles...),
 	}
 }
 
-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, noTidySrcs android.Paths,
+func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs, timeoutTidySrcs 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.WritablePaths
-	noTidySrcsMap := make(map[android.Path]bool)
+	var tidyFiles android.Paths
+	noTidySrcsMap := make(map[string]bool)
 	var tidyVars string
 	if flags.tidy {
-		tidyFiles = make(android.WritablePaths, 0, len(srcFiles))
+		tidyFiles = make(android.Paths, 0, len(srcFiles))
 		for _, path := range noTidySrcs {
-			noTidySrcsMap[path] = true
+			noTidySrcsMap[path.String()] = true
 		}
 		tidyTimeout := ctx.Config().Getenv("TIDY_TIMEOUT")
 		if len(tidyTimeout) > 0 {
-			tidyVars += "TIDY_TIMEOUT=" + tidyTimeout
+			tidyVars += "TIDY_TIMEOUT=" + tidyTimeout + " "
+			// add timeoutTidySrcs into noTidySrcsMap if TIDY_TIMEOUT is set
+			for _, path := range timeoutTidySrcs {
+				noTidySrcsMap[path.String()] = true
+			}
 		}
 	}
 	var coverageFiles android.Paths
@@ -523,11 +539,18 @@
 	cppflags += " ${config.NoOverrideGlobalCflags}"
 	toolingCppflags += " ${config.NoOverrideGlobalCflags}"
 
+	modulePath := android.PathForModuleSrc(ctx).String()
+	if android.IsThirdPartyPath(modulePath) {
+		cflags += " ${config.NoOverrideExternalGlobalCflags}"
+		toolingCflags += " ${config.NoOverrideExternalGlobalCflags}"
+		cppflags += " ${config.NoOverrideExternalGlobalCflags}"
+		toolingCppflags += " ${config.NoOverrideExternalGlobalCflags}"
+	}
+
 	// 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)
+	shared := ctx.getSharedFlags()
 
 	// Share flags only when there are multiple files or tidy rules.
 	var hasMultipleRules = len(srcFiles) > 1 || flags.tidy
@@ -539,11 +562,11 @@
 			return flags
 		}
 		mapKey := kind + flags
-		n, ok := flagsMap[mapKey]
+		n, ok := shared.flagsMap[mapKey]
 		if !ok {
-			numSharedFlags += 1
-			n = strconv.Itoa(numSharedFlags)
-			flagsMap[mapKey] = n
+			shared.numSharedFlags += 1
+			n = strconv.Itoa(shared.numSharedFlags)
+			shared.flagsMap[mapKey] = n
 			ctx.Variable(pctx, kind+n, flags)
 		}
 		return "$" + kind + n
@@ -656,27 +679,46 @@
 		}
 
 		//  Even with tidy, some src file could be skipped by noTidySrcsMap.
-		if tidy && !noTidySrcsMap[srcFile] {
+		if tidy && !noTidySrcsMap[srcFile.String()] {
 			tidyFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy")
+			tidyDepFile := android.ObjPathWithExt(ctx, subdir, srcFile, "tidy.dep")
 			tidyFiles = append(tidyFiles, tidyFile)
 
+			ruleDep := clangTidyDep
 			rule := clangTidy
 			if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CLANG_TIDY") {
+				ruleDep = clangTidyDepRE
 				rule = clangTidyRE
 			}
 
-			ctx.TidyFile(tidyFile)
+			sharedCFlags := shareFlags("cFlags", moduleFlags)
+			srcRelPath := srcFile.Rel()
+
+			// Add the .tidy.d rule
 			ctx.Build(pctx, android.BuildParams{
-				Rule:        rule,
-				Description: "clang-tidy " + srcFile.Rel(),
-				Output:      tidyFile,
+				Rule:        ruleDep,
+				Description: "clang-tidy-dep " + srcRelPath,
+				Output:      tidyDepFile,
 				Input:       srcFile,
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"ccCmd":     ccCmd,
-					"cFlags":    shareFlags("cFlags", escapeSingleQuotes(moduleToolingFlags)),
-					"tidyFlags": shareFlags("tidyFlags", escapeSingleQuotes(config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags))),
+					"ccCmd":    ccCmd,
+					"cFlags":   sharedCFlags,
+					"tidyFile": tidyFile.String(),
+				},
+			})
+			// Add the .tidy rule with order only dependency on the .tidy.d file
+			ctx.Build(pctx, android.BuildParams{
+				Rule:        rule,
+				Description: "clang-tidy " + srcRelPath,
+				Output:      tidyFile,
+				Input:       srcFile,
+				Implicits:   cFlagsDeps,
+				OrderOnly:   append(android.Paths{}, tidyDepFile),
+				Args: map[string]string{
+					"cFlags":    sharedCFlags,
+					"tidyFlags": shareFlags("tidyFlags", config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags)),
 					"tidyVars":  tidyVars, // short and not shared
 				},
 			})
@@ -707,9 +749,14 @@
 
 	}
 
+	var tidyDepFiles android.Paths
+	if flags.needTidyFiles {
+		tidyDepFiles = tidyFiles
+	}
 	return Objects{
 		objFiles:      objFiles,
 		tidyFiles:     tidyFiles,
+		tidyDepFiles:  tidyDepFiles,
 		coverageFiles: coverageFiles,
 		sAbiDumpFiles: sAbiDumpFiles,
 		kytheFiles:    kytheFiles,
@@ -719,12 +766,12 @@
 // Generate a rule for compiling multiple .o files to a static library (.a)
 func transformObjToStaticLib(ctx android.ModuleContext,
 	objFiles android.Paths, wholeStaticLibs android.Paths,
-	flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths, validations android.WritablePaths) {
+	flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths, validations android.Paths) {
 
 	arCmd := "${config.ClangBin}/llvm-ar"
 	arFlags := ""
 	if !ctx.Darwin() {
-		arFlags += " -format=gnu"
+		arFlags += " --format=gnu"
 	}
 
 	if len(wholeStaticLibs) == 0 {
@@ -734,7 +781,7 @@
 			Output:      outputFile,
 			Inputs:      objFiles,
 			Implicits:   deps,
-			Validations: validations.Paths(),
+			Validations: validations,
 			Args: map[string]string{
 				"arFlags": "crsPD" + arFlags,
 				"arCmd":   arCmd,
@@ -764,7 +811,7 @@
 func transformObjToDynamicBinary(ctx android.ModuleContext,
 	objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps, crtBegin, crtEnd android.Paths,
 	groupLate bool, flags builderFlags, outputFile android.WritablePath,
-	implicitOutputs android.WritablePaths, validations android.WritablePaths) {
+	implicitOutputs android.WritablePaths, validations android.Paths) {
 
 	ldCmd := "${config.ClangBin}/clang++"
 
@@ -831,7 +878,7 @@
 		Inputs:          objFiles,
 		Implicits:       deps,
 		OrderOnly:       sharedLibs,
-		Validations:     validations.Paths(),
+		Validations:     validations,
 		Args:            args,
 	})
 }
@@ -1060,6 +1107,15 @@
 	})
 }
 
+func transformDarwinUniversalBinary(ctx android.ModuleContext, outputFile android.WritablePath, inputFiles ...android.Path) {
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        darwinLipo,
+		Description: "lipo " + outputFile.Base(),
+		Output:      outputFile,
+		Inputs:      inputFiles,
+	})
+}
+
 // Registers build statement to zip one or more coverage files.
 func transformCoverageFilesToZip(ctx android.ModuleContext,
 	inputs Objects, baseName string) android.OptionalPath {
diff --git a/cc/cc.go b/cc/cc.go
index 113620c..a8adb0c 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -64,6 +64,9 @@
 
 		ctx.BottomUp("coverage", coverageMutator).Parallel()
 
+		ctx.TopDown("afdo_deps", afdoDepsMutator)
+		ctx.BottomUp("afdo", afdoMutator).Parallel()
+
 		ctx.TopDown("lto_deps", ltoDepsMutator)
 		ctx.BottomUp("lto", ltoMutator).Parallel()
 
@@ -167,6 +170,10 @@
 
 	// Path to the dynamic linker binary
 	DynamicLinker android.OptionalPath
+
+	// For Darwin builds, the path to the second architecture's output that should
+	// be combined with this architectures's output into a FAT MachO file.
+	DarwinSecondArchOutput android.OptionalPath
 }
 
 // LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
@@ -203,11 +210,12 @@
 	// These must be after any module include flags, which will be in CommonFlags.
 	SystemIncludeFlags []string
 
-	Toolchain    config.Toolchain
-	Tidy         bool // True if clang-tidy is enabled.
-	GcovCoverage bool // True if coverage files should be generated.
-	SAbiDump     bool // True if header abi dumps should be generated.
-	EmitXrefs    bool // If true, generate Ninja rules to generate emitXrefs input files for Kythe
+	Toolchain     config.Toolchain
+	Tidy          bool // True if ninja .tidy rules should be generated.
+	NeedTidyFiles bool // True if module link should depend on .tidy files
+	GcovCoverage  bool // True if coverage files should be generated.
+	SAbiDump      bool // True if header abi dumps should be generated.
+	EmitXrefs     bool // If true, generate Ninja rules to generate emitXrefs input files for Kythe
 
 	// The instruction set required for clang ("arm" or "thumb").
 	RequiredInstructionSet string
@@ -497,6 +505,7 @@
 	selectedStl() string
 	baseModuleName() string
 	getVndkExtendsModuleName() string
+	isAfdoCompile() bool
 	isPgoCompile() bool
 	isNDKStubLibrary() bool
 	useClangLld(actx ModuleContext) bool
@@ -509,6 +518,12 @@
 	directlyInAnyApex() bool
 	isPreventInstall() bool
 	isCfiAssemblySupportEnabled() bool
+	getSharedFlags() *SharedFlags
+}
+
+type SharedFlags struct {
+	numSharedFlags int
+	flagsMap       map[string]string
 }
 
 type ModuleContext interface {
@@ -534,8 +549,7 @@
 }
 
 // compiler is the interface for a compiler helper object. Different module decorators may implement
-// this helper differently. For example, compiling a `cc_library` may use a different build
-// statement than building a `toolchain_library`.
+// this helper differently.
 type compiler interface {
 	compilerInit(ctx BaseModuleContext)
 	compilerDeps(ctx DepsContext, deps Deps) Deps
@@ -682,6 +696,15 @@
 	return d.Kind == staticLibraryDependency
 }
 
+func (d libraryDependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
+	if d.shared() {
+		return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency}
+	}
+	return nil
+}
+
+var _ android.LicenseAnnotationsDependencyTag = libraryDependencyTag{}
+
 // InstallDepNeeded returns true for shared libraries so that shared library dependencies of
 // binaries or other shared libraries are installed as dependencies.
 func (d libraryDependencyTag) InstallDepNeeded() bool {
@@ -773,8 +796,9 @@
 	Properties       BaseProperties
 
 	// initialize before calling Init
-	hod      android.HostOrDeviceSupported
-	multilib android.Multilib
+	hod       android.HostOrDeviceSupported
+	multilib  android.Multilib
+	bazelable bool
 
 	// Allowable SdkMemberTypes of this module type.
 	sdkMemberTypes []android.SdkMemberType
@@ -796,6 +820,7 @@
 	sabi     *sabi
 	vndkdep  *vndkdep
 	lto      *lto
+	afdo     *afdo
 	pgo      *pgo
 
 	library libraryInterface
@@ -809,12 +834,19 @@
 	// Flags used to compile this module
 	flags Flags
 
+	// Shared flags among build rules of this module
+	sharedFlags SharedFlags
+
 	// only non-nil when this is a shared library that reuses the objects of a static library
 	staticAnalogue *StaticLibraryInfo
 
 	makeLinkType string
 	// Kythe (source file indexer) paths for this compilation module
 	kytheFiles android.Paths
+	// Object .o file output paths for this compilation module
+	objFiles android.Paths
+	// Tidy .tidy file output paths for this compilation module
+	tidyFiles android.Paths
 
 	// For apex variants, this is set as apex.min_sdk_version
 	apexSdkVersion android.ApiLevel
@@ -947,13 +979,6 @@
 	return ""
 }
 
-func (c *Module) ToolchainLibrary() bool {
-	if _, ok := c.linker.(*toolchainLibraryDecorator); ok {
-		return true
-	}
-	return false
-}
-
 func (c *Module) NdkPrebuiltStl() bool {
 	if _, ok := c.linker.(*ndkPrebuiltStlLinker); ok {
 		return true
@@ -1125,6 +1150,9 @@
 	if c.lto != nil {
 		c.AddProperties(c.lto.props()...)
 	}
+	if c.afdo != nil {
+		c.AddProperties(c.afdo.props()...)
+	}
 	if c.pgo != nil {
 		c.AddProperties(c.pgo.props()...)
 	}
@@ -1133,7 +1161,9 @@
 	}
 
 	android.InitAndroidArchModule(c, c.hod, c.multilib)
-	android.InitBazelModule(c)
+	if c.bazelable {
+		android.InitBazelModule(c)
+	}
 	android.InitApexModule(c)
 	android.InitSdkAwareModule(c)
 	android.InitDefaultableModule(c)
@@ -1230,6 +1260,13 @@
 	return false
 }
 
+func (c *Module) isAfdoCompile() bool {
+	if afdo := c.afdo; afdo != nil {
+		return afdo.Properties.AfdoTarget != nil
+	}
+	return false
+}
+
 func (c *Module) isPgoCompile() bool {
 	if pgo := c.pgo; pgo != nil {
 		return pgo.Properties.PgoCompile
@@ -1365,8 +1402,6 @@
 	return c.installer != nil && c.installer.installInRoot()
 }
 
-func (c *Module) InstallBypassMake() bool { return true }
-
 type baseModuleContext struct {
 	android.BaseModuleContext
 	moduleContextImpl
@@ -1509,6 +1544,10 @@
 	return ctx.mod.IsVndk()
 }
 
+func (ctx *moduleContextImpl) isAfdoCompile() bool {
+	return ctx.mod.isAfdoCompile()
+}
+
 func (ctx *moduleContextImpl) isPgoCompile() bool {
 	return ctx.mod.isPgoCompile()
 }
@@ -1580,6 +1619,15 @@
 	return ctx.mod.Properties.PreventInstall
 }
 
+func (ctx *moduleContextImpl) getSharedFlags() *SharedFlags {
+	shared := &ctx.mod.sharedFlags
+	if shared.flagsMap == nil {
+		shared.numSharedFlags = 0
+		shared.flagsMap = make(map[string]string)
+	}
+	return shared
+}
+
 func (ctx *moduleContextImpl) isCfiAssemblySupportEnabled() bool {
 	return ctx.mod.isCfiAssemblySupportEnabled()
 }
@@ -1602,6 +1650,7 @@
 	module.sabi = &sabi{}
 	module.vndkdep = &vndkdep{}
 	module.lto = &lto{}
+	module.afdo = &afdo{}
 	module.pgo = &pgo{}
 	return module
 }
@@ -1713,7 +1762,16 @@
 
 // Returns true if Bazel was successfully used for the analysis of this module.
 func (c *Module) maybeGenerateBazelActions(actx android.ModuleContext) bool {
-	bazelModuleLabel := c.GetBazelLabel(actx, c)
+	var bazelModuleLabel string
+	if c.typ() == fullLibrary && c.static() {
+		// cc_library is a special case in bp2build; two targets are generated -- one for each
+		// of the shared and static variants. The shared variant keeps the module name, but the
+		// static variant uses a different suffixed name.
+		bazelModuleLabel = bazelLabelForStaticModule(actx, c)
+	} else {
+		bazelModuleLabel = c.GetBazelLabel(actx, c)
+	}
+
 	bazelActionsUsed := false
 	// Mixed builds mode is disabled for modules outside of device OS.
 	// TODO(b/200841190): Support non-device OS in mixed builds.
@@ -1789,6 +1847,9 @@
 	if c.lto != nil {
 		flags = c.lto.flags(ctx, flags)
 	}
+	if c.afdo != nil {
+		flags = c.afdo.flags(ctx, flags)
+	}
 	if c.pgo != nil {
 		flags = c.pgo.flags(ctx, flags)
 	}
@@ -1827,6 +1888,8 @@
 			return
 		}
 		c.kytheFiles = objs.kytheFiles
+		c.objFiles = objs.objFiles
+		c.tidyFiles = objs.tidyFiles
 	}
 
 	if c.linker != nil {
@@ -1914,6 +1977,9 @@
 	if c.lto != nil {
 		c.lto.begin(ctx)
 	}
+	if c.afdo != nil {
+		c.afdo.begin(ctx)
+	}
 	if c.pgo != nil {
 		c.pgo.begin(ctx)
 	}
@@ -2389,10 +2455,6 @@
 		return
 	}
 	if c, ok := to.(*Module); ok {
-		if c.ToolchainLibrary() {
-			// These are always allowed
-			return
-		}
 		if c.NdkPrebuiltStl() {
 			// These are allowed, but they don't set sdk_version
 			return
@@ -2570,6 +2632,11 @@
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
 
+		if depTag == android.DarwinUniversalVariantTag {
+			depPaths.DarwinSecondArchOutput = dep.(*Module).OutputFile()
+			return
+		}
+
 		ccDep, ok := dep.(LinkableInterface)
 		if !ok {
 
@@ -2740,6 +2807,8 @@
 						// dependency.
 						depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
 					}
+					depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts,
+						staticLibraryInfo.WholeStaticLibsFromPrebuilts...)
 				} else {
 					switch libDepTag.Order {
 					case earlyLibraryDependency:
@@ -3153,6 +3222,24 @@
 	return false
 }
 
+func (c *Module) benchmarkBinary() bool {
+	if b, ok := c.linker.(interface {
+		benchmarkBinary() bool
+	}); ok {
+		return b.benchmarkBinary()
+	}
+	return false
+}
+
+func (c *Module) fuzzBinary() bool {
+	if f, ok := c.linker.(interface {
+		fuzzBinary() bool
+	}); ok {
+		return f.fuzzBinary()
+	}
+	return false
+}
+
 // Header returns true if the module is a header-only variant. (See cc/library.go header()).
 func (c *Module) Header() bool {
 	if h, ok := c.linker.(interface {
@@ -3355,10 +3442,6 @@
 	if strings.HasPrefix(ctx.OtherModuleName(c), "libclang_rt") {
 		return nil
 	}
-	// b/154569636: set min_sdk_version correctly for toolchain_libraries
-	if c.ToolchainLibrary() {
-		return nil
-	}
 	// We don't check for prebuilt modules
 	if _, ok := c.linker.(prebuiltLinkerInterface); ok {
 		return nil
@@ -3396,18 +3479,95 @@
 	return c.IsStubs() || c.Target().NativeBridge == android.NativeBridgeEnabled
 }
 
+// Overrides android.ApexModuleBase.UniqueApexVariations
+func (c *Module) UniqueApexVariations() bool {
+	// When a vendor APEX needs a VNDK lib in it (use_vndk_as_stable: false), it should be a unique
+	// APEX variation. Otherwise, another vendor APEX with use_vndk_as_stable:true may use a wrong
+	// variation of the VNDK lib because APEX variations are merged/grouped.
+	return c.UseVndk() && c.IsVndk()
+}
+
 var _ snapshot.RelativeInstallPath = (*Module)(nil)
 
+type moduleType int
+
+const (
+	unknownType moduleType = iota
+	binary
+	object
+	fullLibrary
+	staticLibrary
+	sharedLibrary
+	headerLibrary
+)
+
+func (c *Module) typ() moduleType {
+	if c.Binary() {
+		return binary
+	} else if c.Object() {
+		return object
+	} else if c.CcLibrary() {
+		static := false
+		shared := false
+		if library, ok := c.linker.(*libraryDecorator); ok {
+			static = library.MutatedProperties.BuildStatic
+			shared = library.MutatedProperties.BuildShared
+		} else if library, ok := c.linker.(*prebuiltLibraryLinker); ok {
+			static = library.MutatedProperties.BuildStatic
+			shared = library.MutatedProperties.BuildShared
+		}
+		if static && shared {
+			return fullLibrary
+		} else if !static && !shared {
+			return headerLibrary
+		} else if static {
+			return staticLibrary
+		}
+		return sharedLibrary
+	}
+	return unknownType
+}
+
+// ConvertWithBp2build converts Module to Bazel for bp2build.
+func (c *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	prebuilt := c.IsPrebuilt()
+	switch c.typ() {
+	case binary:
+		if !prebuilt {
+			binaryBp2build(ctx, c, ctx.ModuleType())
+		}
+	case object:
+		if !prebuilt {
+			objectBp2Build(ctx, c)
+		}
+	case fullLibrary:
+		if !prebuilt {
+			libraryBp2Build(ctx, c)
+		}
+	case headerLibrary:
+		libraryHeadersBp2Build(ctx, c)
+	case staticLibrary:
+
+		if prebuilt {
+			prebuiltLibraryStaticBp2Build(ctx, c)
+		} else {
+			sharedOrStaticLibraryBp2Build(ctx, c, true)
+		}
+	case sharedLibrary:
+		if prebuilt {
+			prebuiltLibrarySharedBp2Build(ctx, c)
+		} else {
+			sharedOrStaticLibraryBp2Build(ctx, c, false)
+		}
+	}
+}
+
 //
 // Defaults
 //
 type Defaults struct {
 	android.ModuleBase
 	android.DefaultsModuleBase
-	// Included to support setting bazel_module.label for multiple Soong modules to the same Bazel
-	// target. This is primarily useful for modules that were architecture specific and instead are
-	// handled in Bazel as a select().
-	android.BazelModuleBase
 	android.ApexModuleBase
 }
 
@@ -3448,6 +3608,7 @@
 		&SAbiProperties{},
 		&VndkProperties{},
 		&LTOProperties{},
+		&AfdoProperties{},
 		&PgoProperties{},
 		&android.ProtoProperties{},
 		// RustBindgenProperties is included here so that cc_defaults can be used for rust_bindgen modules.
@@ -3455,8 +3616,6 @@
 		&prebuiltLinkerProperties{},
 	)
 
-	// Bazel module must be initialized _before_ Defaults to be included in cc_defaults module.
-	android.InitBazelModule(module)
 	android.InitDefaultsModule(module)
 
 	return module
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 9ffe48d..51a6a27 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -3943,7 +3943,6 @@
 		"${config.ArmGenericCflags}",
 		"-target",
 		"armv7a-linux-androideabi20",
-		"-B${config.ArmGccRoot}/arm-linux-androideabi/bin",
 	}
 
 	expectedIncludes := []string{
@@ -3980,9 +3979,9 @@
 	conly := []string{"-fPIC", "${config.CommonGlobalConlyflags}"}
 	cppOnly := []string{"-fPIC", "${config.CommonGlobalCppflags}", "${config.DeviceGlobalCppflags}", "${config.ArmCppflags}"}
 
-	cflags := []string{"-Wall", "-Werror"}
-	cstd := []string{"-std=gnu99"}
-	cppstd := []string{"-std=gnu++17", "-fno-rtti"}
+	cflags := []string{"-Wall", "-Werror", "-std=candcpp"}
+	cstd := []string{"-std=gnu99", "-std=conly"}
+	cppstd := []string{"-std=gnu++17", "-std=cpp", "-fno-rtti"}
 
 	lastIncludes := []string{
 		"out/soong/ndk/sysroot/usr/include",
@@ -4005,12 +4004,12 @@
 		{
 			name:     "c",
 			src:      "foo.c",
-			expected: combineSlices(baseExpectedFlags, conly, expectedIncludes, cflags, cstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}"}),
+			expected: combineSlices(baseExpectedFlags, conly, expectedIncludes, cflags, cstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"}),
 		},
 		{
 			name:     "cc",
 			src:      "foo.cc",
-			expected: combineSlices(baseExpectedFlags, cppOnly, expectedIncludes, cflags, cppstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}"}),
+			expected: combineSlices(baseExpectedFlags, cppOnly, expectedIncludes, cflags, cppstd, lastIncludes, []string{"${config.NoOverrideGlobalCflags}", "${config.NoOverrideExternalGlobalCflags}"}),
 		},
 		{
 			name:     "assemble",
@@ -4025,6 +4024,9 @@
 		cc_library {
 			name: "libfoo",
 			srcs: ["%s"],
+			cflags: ["-std=candcpp"],
+			conlyflags: ["-std=conly"],
+			cppflags: ["-std=cpp"],
 			local_include_dirs: ["local_include_dirs"],
 			export_include_dirs: ["export_include_dirs"],
 			export_system_include_dirs: ["export_system_include_dirs"],
diff --git a/cc/compiler.go b/cc/compiler.go
index dca6a3c..eb5458f 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -42,6 +42,9 @@
 	// 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 compiled by clang-tidy when TIDY_TIMEOUT is set.
+	Tidy_timeout_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"`
@@ -454,11 +457,9 @@
 		}
 	}
 
-	gccPrefix := "-B" + config.ToolPath(tc)
-
-	flags.Global.CFlags = append(flags.Global.CFlags, target, gccPrefix)
-	flags.Global.AsFlags = append(flags.Global.AsFlags, target, gccPrefix)
-	flags.Global.LdFlags = append(flags.Global.LdFlags, target, gccPrefix)
+	flags.Global.CFlags = append(flags.Global.CFlags, target)
+	flags.Global.AsFlags = append(flags.Global.AsFlags, target)
+	flags.Global.LdFlags = append(flags.Global.LdFlags, target)
 
 	hod := "Host"
 	if ctx.Os().Class == android.Device {
@@ -641,9 +642,9 @@
 
 func ndkPathDeps(ctx ModuleContext) android.Paths {
 	if ctx.Module().(*Module).IsSdkVariant() {
-		// The NDK sysroot timestamp file depends on all the NDK sysroot files
-		// (headers and libraries).
-		return android.Paths{getNdkBaseTimestampFile(ctx)}
+		// The NDK sysroot timestamp file depends on all the NDK sysroot header files
+		// for compiling src to obj files.
+		return android.Paths{getNdkHeadersTimestampFile(ctx)}
 	}
 	return nil
 }
@@ -669,6 +670,7 @@
 	// Compile files listed in c.Properties.Srcs into objects
 	objs := compileObjs(ctx, buildFlags, "", srcs,
 		android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_disabled_srcs),
+		android.PathsForModuleSrc(ctx, compiler.Properties.Tidy_timeout_srcs),
 		pathDeps, compiler.cFlagsDeps)
 
 	if ctx.Failed() {
@@ -679,10 +681,10 @@
 }
 
 // Compile a list of source files into objects a specified subdirectory
-func compileObjs(ctx android.ModuleContext, flags builderFlags, subdir string,
-	srcFiles, noTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
+func compileObjs(ctx ModuleContext, flags builderFlags, subdir string,
+	srcFiles, noTidySrcs, timeoutTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
 
-	return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, flags, pathDeps, cFlagsDeps)
+	return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, timeoutTidySrcs, flags, pathDeps, cFlagsDeps)
 }
 
 // Properties for rust_bindgen related to generating rust bindings.
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 7b7ee28..e1b0605 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -8,6 +8,7 @@
     deps: [
         "soong-android",
         "soong-remoteexec",
+        "soong-starlark-format",
     ],
     srcs: [
         "bp2build.go",
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 2d6bcb8..979c825 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -33,7 +33,9 @@
 		},
 		"armv8-a-branchprot": []string{
 			"-march=armv8-a",
-			"-mbranch-protection=standard",
+			// Disable BTI until drm vendors stop using OS libraries as sources
+			// of gadgets (https://issuetracker.google.com/216395195).
+			"-mbranch-protection=pac-ret",
 		},
 		"armv8-2a": []string{
 			"-march=armv8.2-a",
diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go
index 853d818..5c7f926 100644
--- a/cc/config/arm64_linux_host.go
+++ b/cc/config/arm64_linux_host.go
@@ -41,7 +41,6 @@
 		"-Wl,-z,relro",
 		"-Wl,-z,now",
 		"-Wl,--build-id=md5",
-		"-Wl,--warn-shared-textrel",
 		"-Wl,--fatal-warnings",
 		"-Wl,--hash-style=gnu",
 		"-Wl,--no-undefined-version",
diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go
index 4797acc..eca5161 100644
--- a/cc/config/bp2build.go
+++ b/cc/config/bp2build.go
@@ -22,11 +22,9 @@
 	"strings"
 
 	"android/soong/android"
-	"github.com/google/blueprint"
-)
+	"android/soong/starlark_fmt"
 
-const (
-	bazelIndent = 4
+	"github.com/google/blueprint"
 )
 
 type bazelVarExporter interface {
@@ -72,21 +70,6 @@
 	m[k] = v
 }
 
-func bazelIndention(level int) string {
-	return strings.Repeat(" ", level*bazelIndent)
-}
-
-func printBazelList(items []string, indentLevel int) string {
-	list := make([]string, 0, len(items)+2)
-	list = append(list, "[")
-	innerIndent := bazelIndention(indentLevel + 1)
-	for _, item := range items {
-		list = append(list, fmt.Sprintf(`%s"%s",`, innerIndent, item))
-	}
-	list = append(list, bazelIndention(indentLevel)+"]")
-	return strings.Join(list, "\n")
-}
-
 func (m exportedStringVariables) asBazel(config android.Config,
 	stringVars exportedStringVariables, stringListVars exportedStringListVariables, cfgDepVars exportedConfigDependingVariables) []bazelConstant {
 	ret := make([]bazelConstant, 0, len(m))
@@ -138,7 +121,7 @@
 		// out through a constants struct later.
 		ret = append(ret, bazelConstant{
 			variableName:       k,
-			internalDefinition: printBazelList(expandedVars, 0),
+			internalDefinition: starlark_fmt.PrintStringList(expandedVars, 0),
 		})
 	}
 	return ret
@@ -162,23 +145,16 @@
 	exportedStringListVars.Set(name, value)
 }
 
+func ExportStringList(name string, value []string) {
+	exportedStringListVars.Set(name, value)
+}
+
 type exportedStringListDictVariables map[string]map[string][]string
 
 func (m exportedStringListDictVariables) Set(k string, v map[string][]string) {
 	m[k] = v
 }
 
-func printBazelStringListDict(dict map[string][]string) string {
-	bazelDict := make([]string, 0, len(dict)+2)
-	bazelDict = append(bazelDict, "{")
-	for k, v := range dict {
-		bazelDict = append(bazelDict,
-			fmt.Sprintf(`%s"%s": %s,`, bazelIndention(1), k, printBazelList(v, 1)))
-	}
-	bazelDict = append(bazelDict, "}")
-	return strings.Join(bazelDict, "\n")
-}
-
 // Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
 func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
 	_ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
@@ -186,7 +162,7 @@
 	for k, dict := range m {
 		ret = append(ret, bazelConstant{
 			variableName:       k,
-			internalDefinition: printBazelStringListDict(dict),
+			internalDefinition: starlark_fmt.PrintStringListDict(dict, 0),
 		})
 	}
 	return ret
@@ -218,7 +194,7 @@
 		definitions = append(definitions,
 			fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
 		constants = append(constants,
-			fmt.Sprintf("%[1]s%[2]s = _%[2]s,", bazelIndention(1), b.variableName))
+			fmt.Sprintf("%[1]s%[2]s = _%[2]s,", starlark_fmt.Indention(1), b.variableName))
 	}
 
 	// Build the exported constants struct.
diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go
index 3118df1..4cbf0c6 100644
--- a/cc/config/bp2build_test.go
+++ b/cc/config/bp2build_test.go
@@ -211,15 +211,11 @@
 			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
 
 _a = {
-    "b1": [
-        "b2",
-    ],
+    "b1": ["b2"],
 }
 
 _c = {
-    "d1": [
-        "d2",
-    ],
+    "d1": ["d2"],
 }
 
 constants = struct(
@@ -246,27 +242,19 @@
 			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
 
 _a = {
-    "a1": [
-        "a2",
-    ],
+    "a1": ["a2"],
 }
 
 _b = "b-val"
 
-_c = [
-    "c-val",
-]
+_c = ["c-val"]
 
 _d = "d-val"
 
-_e = [
-    "e-val",
-]
+_e = ["e-val"]
 
 _f = {
-    "f1": [
-        "f2",
-    ],
+    "f1": ["f2"],
 }
 
 constants = struct(
diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go
index 318acb4..5e3f7c7 100644
--- a/cc/config/darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -54,6 +54,7 @@
 
 	darwinSupportedSdkVersions = []string{
 		"11",
+		"12",
 	}
 
 	darwinAvailableLibraries = append(
@@ -87,6 +88,10 @@
 		return getMacTools(ctx).arPath
 	})
 
+	pctx.VariableFunc("MacLipoPath", func(ctx android.PackageVarContext) string {
+		return getMacTools(ctx).lipoPath
+	})
+
 	pctx.VariableFunc("MacStripPath", func(ctx android.PackageVarContext) string {
 		return getMacTools(ctx).stripPath
 	})
@@ -118,6 +123,7 @@
 
 	sdkRoot   string
 	arPath    string
+	lipoPath  string
 	stripPath string
 	toolPath  string
 }
@@ -157,6 +163,7 @@
 		macTools.sdkRoot = xcrun("--show-sdk-path")
 
 		macTools.arPath = xcrun("--find", "ar")
+		macTools.lipoPath = xcrun("--find", "lipo")
 		macTools.stripPath = xcrun("--find", "strip")
 		macTools.toolPath = filepath.Dir(xcrun("--find", "ld"))
 	})
@@ -251,8 +258,12 @@
 	return darwinAvailableLibraries
 }
 
-func (t *toolchainDarwin) ToolPath() string {
-	return "${config.MacToolPath}"
+func (t *toolchainDarwin) ToolchainCflags() string {
+	return "-B${config.MacToolPath}"
+}
+
+func (t *toolchainDarwin) ToolchainLdflags() string {
+	return "-B${config.MacToolPath}"
 }
 
 var toolchainDarwinArmSingleton Toolchain = &toolchainDarwinArm{}
diff --git a/cc/config/global.go b/cc/config/global.go
index 0b229be..400be31 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -46,7 +46,7 @@
 
 		"-O2",
 		"-g",
-		"-fdebug-info-for-profiling",
+		"-fdebug-default-version=5",
 
 		"-fno-strict-aliasing",
 
@@ -95,6 +95,9 @@
 		// Nested and array designated initialization is nice to have.
 		"-Wno-c99-designator",
 
+		// Many old files still have GNU designator syntax.
+		"-Wno-gnu-designator",
+
 		// Warnings from clang-12
 		"-Wno-gnu-folding-constant",
 
@@ -104,6 +107,9 @@
 		// This macro allows the bionic versioning.h to indirectly determine whether the
 		// option -Wunguarded-availability is on or not.
 		"-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__",
+
+		// Turn off FMA which got enabled by default in clang-r445002 (http://b/218805949)
+		"-ffp-contract=off",
 	}
 
 	commonGlobalConlyflags = []string{}
@@ -125,6 +131,9 @@
 		"-Werror=sequence-point",
 		"-Werror=format-security",
 		"-nostdlibinc",
+
+		// Emit additional debug info for AutoFDO
+		"-fdebug-info-for-profiling",
 	}
 
 	deviceGlobalCppflags = []string{
@@ -136,7 +145,6 @@
 		"-Wl,-z,relro",
 		"-Wl,-z,now",
 		"-Wl,--build-id=md5",
-		"-Wl,--warn-shared-textrel",
 		"-Wl,--fatal-warnings",
 		"-Wl,--no-undefined-version",
 		// TODO: Eventually we should link against a libunwind.a with hidden symbols, and then these
@@ -222,8 +230,6 @@
 		"-Wno-pessimizing-move",                     // http://b/154270751
 		// New warnings to be fixed after clang-r399163
 		"-Wno-non-c-typedef-for-linkage", // http://b/161304145
-		// New warnings to be fixed after clang-r407598
-		"-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
@@ -231,6 +237,14 @@
 		"-Wno-error=unused-but-set-parameter", // http://b/197240255
 	}
 
+	noOverrideExternalGlobalCflags = []string{
+		// http://b/197240255
+		"-Wno-unused-but-set-variable",
+		"-Wno-unused-but-set-parameter",
+		// http://b/215753485
+		"-Wno-bitwise-instead-of-logical",
+	}
+
 	// Extra cflags for external third-party projects to disable warnings that
 	// are infeasible to fix in all the external projects and their upstream repos.
 	extraExternalCflags = []string{
@@ -257,6 +271,9 @@
 
 		// http://b/199369603
 		"-Wno-null-pointer-subtraction",
+
+		// http://b/175068488
+		"-Wno-string-concatenation",
 	}
 
 	IllegalFlags = []string{
@@ -270,8 +287,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r437112"
-	ClangDefaultShortVersion = "14.0.0"
+	ClangDefaultVersion      = "clang-r445002"
+	ClangDefaultShortVersion = "14.0.2"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
@@ -344,6 +361,7 @@
 
 	exportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags)
 	exportStringListStaticVariable("NoOverrideGlobalCflags", noOverrideGlobalCflags)
+	exportStringListStaticVariable("NoOverrideExternalGlobalCflags", noOverrideExternalGlobalCflags)
 	exportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
 	exportStringListStaticVariable("ExternalCflags", extraExternalCflags)
 
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index fdc246c..ba1043b 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -62,8 +62,9 @@
 		}, ",")
 		// clang-analyzer-* checks are too slow to be in the default for WITH_TIDY=1.
 		// nightly builds add CLANG_ANALYZER_CHECKS=1 to run those checks.
+		// The insecureAPI.DeprecatedOrUnsafeBufferHandling warning does not apply to Android.
 		if ctx.Config().IsEnvTrue("CLANG_ANALYZER_CHECKS") {
-			checks += ",clang-analyzer-*"
+			checks += ",clang-analyzer-*,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling"
 		}
 		return checks
 	})
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 6320dbb..6cede11 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"path/filepath"
 
 	"android/soong/android"
 )
@@ -77,7 +76,6 @@
 	GccTriple() string
 	// GccVersion should return a real value, not a ninja reference
 	GccVersion() string
-	ToolPath() string
 
 	IncludeFlags() string
 
@@ -198,10 +196,6 @@
 	return false
 }
 
-func (t toolchainBase) ToolPath() string {
-	return ""
-}
-
 type toolchain64Bit struct {
 	toolchainBase
 }
@@ -283,11 +277,4 @@
 	return LibclangRuntimeLibrary(t, "fuzzer")
 }
 
-func ToolPath(t Toolchain) string {
-	if p := t.ToolPath(); p != "" {
-		return p
-	}
-	return filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
-}
-
 var inList = android.InList
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 00f07ff..d789cde 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"fmt"
 	"strings"
 
 	"android/soong/android"
@@ -190,6 +191,11 @@
 }
 
 func x86_64ToolchainFactory(arch android.Arch) Toolchain {
+	// Error now rather than having a confusing Ninja error
+	if _, ok := x86_64ArchVariantCflags[arch.ArchVariant]; !ok {
+		panic(fmt.Sprintf("Unknown x86_64 architecture version: %q", arch.ArchVariant))
+	}
+
 	toolchainCflags := []string{
 		"${config.X86_64ToolchainCflags}",
 		"${config.X86_64" + arch.ArchVariant + "VariantCflags}",
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index 29f0593..e32e1bd 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"fmt"
 	"strings"
 
 	"android/soong/android"
@@ -186,6 +187,11 @@
 }
 
 func x86ToolchainFactory(arch android.Arch) Toolchain {
+	// Error now rather than having a confusing Ninja error
+	if _, ok := x86ArchVariantCflags[arch.ArchVariant]; !ok {
+		panic(fmt.Sprintf("Unknown x86 architecture version: %q", arch.ArchVariant))
+	}
+
 	toolchainCflags := []string{
 		"${config.X86ToolchainCflags}",
 		"${config.X86" + arch.ArchVariant + "VariantCflags}",
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index 4b7ba6a..976cc25 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -47,7 +47,6 @@
 		"-Wl,-z,relro",
 		"-Wl,-z,now",
 		"-Wl,--build-id=md5",
-		"-Wl,--warn-shared-textrel",
 		"-Wl,--fatal-warnings",
 		"-Wl,--hash-style=gnu",
 		"-Wl,--no-undefined-version",
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 43333fa..ce6836b 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -47,6 +47,7 @@
 		"-D_LIBCPP_HAS_MUSL_LIBC",
 		"-DANDROID_HOST_MUSL",
 		"-nostdlibinc",
+		"--sysroot /dev/null",
 	}
 
 	linuxLdflags = []string{
@@ -65,6 +66,7 @@
 	linuxMuslLdflags = []string{
 		"-nostdlib",
 		"-lgcc", "-lgcc_eh",
+		"--sysroot /dev/null",
 	}
 
 	// Extended cflags
@@ -194,10 +196,6 @@
 	return ""
 }
 
-func (t *toolchainLinuxX86) ClangTriple() string {
-	return "i686-linux-gnu"
-}
-
 func (t *toolchainLinuxX86) Cflags() string {
 	return "${config.LinuxCflags} ${config.LinuxX86Cflags}"
 }
@@ -206,10 +204,6 @@
 	return ""
 }
 
-func (t *toolchainLinuxX8664) ClangTriple() string {
-	return "x86_64-linux-gnu"
-}
-
 func (t *toolchainLinuxX8664) Cflags() string {
 	return "${config.LinuxCflags} ${config.LinuxX8664Cflags}"
 }
@@ -283,6 +277,10 @@
 	toolchainGlibc
 }
 
+func (t *toolchainLinuxGlibcX86) ClangTriple() string {
+	return "i686-linux-gnu"
+}
+
 func (t *toolchainLinuxGlibcX86) Cflags() string {
 	return t.toolchainLinuxX86.Cflags() + " " + t.toolchainGlibc.Cflags()
 }
@@ -295,6 +293,10 @@
 	return t.toolchainLinuxX86.Lldflags() + " " + t.toolchainGlibc.Lldflags()
 }
 
+func (t *toolchainLinuxGlibcX8664) ClangTriple() string {
+	return "x86_64-linux-gnu"
+}
+
 func (t *toolchainLinuxGlibcX8664) Cflags() string {
 	return t.toolchainLinuxX8664.Cflags() + " " + t.toolchainGlibc.Cflags()
 }
@@ -356,6 +358,10 @@
 	toolchainMusl
 }
 
+func (t *toolchainLinuxMuslX86) ClangTriple() string {
+	return "i686-linux-musl"
+}
+
 func (t *toolchainLinuxMuslX86) Cflags() string {
 	return t.toolchainLinuxX86.Cflags() + " " + t.toolchainMusl.Cflags()
 }
@@ -368,6 +374,10 @@
 	return t.toolchainLinuxX86.Lldflags() + " " + t.toolchainMusl.Lldflags()
 }
 
+func (t *toolchainLinuxMuslX8664) ClangTriple() string {
+	return "x86_64-linux-musl"
+}
+
 func (t *toolchainLinuxMuslX8664) Cflags() string {
 	return t.toolchainLinuxX8664.Cflags() + " " + t.toolchainMusl.Cflags()
 }
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 9daf40f..2c83211 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"path/filepath"
 	"strings"
 
 	"android/soong/android"
@@ -180,6 +181,14 @@
 	return "${config.WindowsGccTriple}"
 }
 
+func (t *toolchainWindows) ToolchainCflags() string {
+	return "-B" + filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
+}
+
+func (t *toolchainWindows) ToolchainLdflags() string {
+	return "-B" + filepath.Join(t.GccRoot(), t.GccTriple(), "bin")
+}
+
 func (t *toolchainWindows) GccVersion() string {
 	return windowsGccVersion
 }
diff --git a/cc/coverage.go b/cc/coverage.go
index 8dd2db1..f2b5425 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -98,6 +98,9 @@
 		} else if clangCoverage {
 			flags.Local.CommonFlags = append(flags.Local.CommonFlags, profileInstrFlag,
 				"-fcoverage-mapping", "-Wno-pass-failed", "-D__ANDROID_CLANG_COVERAGE__")
+			// Override -Wframe-larger-than.  We can expect frame size increase after
+			// coverage instrumentation.
+			flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=")
 		}
 	}
 
diff --git a/cc/fuzz.go b/cc/fuzz.go
index e987fe4..23d81d6 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -52,6 +52,10 @@
 	installedSharedDeps []string
 }
 
+func (fuzz *fuzzBinary) fuzzBinary() bool {
+	return true
+}
+
 func (fuzz *fuzzBinary) linkerProps() []interface{} {
 	props := fuzz.binaryDecorator.linkerProps()
 	props = append(props, &fuzz.fuzzPackagedModule.FuzzProperties)
@@ -234,7 +238,7 @@
 }
 
 func NewFuzz(hod android.HostOrDeviceSupported) *Module {
-	module, binary := NewBinary(hod)
+	module, binary := newBinary(hod, false)
 
 	binary.baseInstaller = NewFuzzInstaller()
 	module.sanitize.SetSanitizer(Fuzzer, true)
diff --git a/cc/libbuildversion/Android.bp b/cc/libbuildversion/Android.bp
index 2cff994..b105a30 100644
--- a/cc/libbuildversion/Android.bp
+++ b/cc/libbuildversion/Android.bp
@@ -19,4 +19,5 @@
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
+    vendor_available: true,
 }
diff --git a/cc/libbuildversion/libbuildversion.cpp b/cc/libbuildversion/libbuildversion.cpp
index 5242025..1e01c11 100644
--- a/cc/libbuildversion/libbuildversion.cpp
+++ b/cc/libbuildversion/libbuildversion.cpp
@@ -36,7 +36,11 @@
     return soong_build_number;
   }
 
+#ifdef __ANDROID_VENDOR__
+  const prop_info* pi = __system_property_find("ro.vendor.build.version.incremental");
+#else
   const prop_info* pi = __system_property_find("ro.build.version.incremental");
+#endif
   if (pi == nullptr) return "";
 
   std::string property_value;
diff --git a/cc/library.go b/cc/library.go
index 3dceda0..5fa3471 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -32,7 +32,7 @@
 	"github.com/google/blueprint/pathtools"
 )
 
-// LibraryProperties is a collection of properties shared by cc library rules.
+// LibraryProperties is a collection of properties shared by cc library rules/cc.
 type LibraryProperties struct {
 	// local file name to pass to the linker as -unexported_symbols_list
 	Unexported_symbols_list *string `android:"path,arch_variant"`
@@ -145,6 +145,8 @@
 
 	Tidy_disabled_srcs []string `android:"path,arch_variant"`
 
+	Tidy_timeout_srcs []string `android:"path,arch_variant"`
+
 	Sanitized Sanitized `android:"arch_variant"`
 
 	Cflags []string `android:"arch_variant"`
@@ -207,10 +209,6 @@
 
 func init() {
 	RegisterLibraryBuildComponents(android.InitRegistrationContext)
-
-	android.RegisterBp2BuildMutator("cc_library_static", CcLibraryStaticBp2Build)
-	android.RegisterBp2BuildMutator("cc_library_shared", CcLibrarySharedBp2Build)
-	android.RegisterBp2BuildMutator("cc_library", CcLibraryBp2Build)
 }
 
 func RegisterLibraryBuildComponents(ctx android.RegistrationContext) {
@@ -236,12 +234,13 @@
 
 	Hdrs bazel.LabelListAttribute
 
-	Deps                        bazel.LabelListAttribute
-	Implementation_deps         bazel.LabelListAttribute
-	Dynamic_deps                bazel.LabelListAttribute
-	Implementation_dynamic_deps bazel.LabelListAttribute
-	Whole_archive_deps          bazel.LabelListAttribute
-	System_dynamic_deps         bazel.LabelListAttribute
+	Deps                              bazel.LabelListAttribute
+	Implementation_deps               bazel.LabelListAttribute
+	Dynamic_deps                      bazel.LabelListAttribute
+	Implementation_dynamic_deps       bazel.LabelListAttribute
+	Whole_archive_deps                bazel.LabelListAttribute
+	Implementation_whole_archive_deps bazel.LabelListAttribute
+	System_dynamic_deps               bazel.LabelListAttribute
 
 	Export_includes        bazel.StringListAttribute
 	Export_system_includes bazel.StringListAttribute
@@ -276,21 +275,12 @@
 	None                         bazel.BoolAttribute
 }
 
-func CcLibraryBp2Build(ctx android.TopDownMutatorContext) {
-	m, ok := ctx.Module().(*Module)
-	if !ok || !m.ConvertWithBp2build(ctx) {
-		return
-	}
-
-	if ctx.ModuleType() != "cc_library" {
-		return
-	}
-
+func libraryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
 	// 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.Module().Name()) {
-		ccSharedOrStaticBp2BuildMutatorInternal(ctx, m, "cc_library_static")
+		sharedOrStaticLibraryBp2Build(ctx, m, true)
 		return
 	}
 
@@ -299,44 +289,89 @@
 	baseAttributes := bp2BuildParseBaseProps(ctx, m)
 	compilerAttrs := baseAttributes.compilerAttributes
 	linkerAttrs := baseAttributes.linkerAttributes
-	exportedIncludes := bp2BuildParseExportedIncludes(ctx, m)
+	exportedIncludes := bp2BuildParseExportedIncludes(ctx, m, compilerAttrs.includes)
 
 	srcs := compilerAttrs.srcs
 
+	sharedAttrs.Dynamic_deps.Add(baseAttributes.protoDependency)
+	staticAttrs.Deps.Add(baseAttributes.protoDependency)
+
 	asFlags := compilerAttrs.asFlags
 	if compilerAttrs.asSrcs.IsEmpty() && sharedAttrs.Srcs_as.IsEmpty() && staticAttrs.Srcs_as.IsEmpty() {
 		// Skip asflags for BUILD file simplicity if there are no assembly sources.
 		asFlags = bazel.MakeStringListAttribute(nil)
 	}
 
-	attrs := &bazelCcLibraryAttributes{
-		Srcs:    srcs,
-		Srcs_c:  compilerAttrs.cSrcs,
-		Srcs_as: compilerAttrs.asSrcs,
-		Hdrs:    compilerAttrs.hdrs,
+	staticCommonAttrs := staticOrSharedAttributes{
+		Srcs:    *srcs.Clone().Append(staticAttrs.Srcs),
+		Srcs_c:  *compilerAttrs.cSrcs.Clone().Append(staticAttrs.Srcs_c),
+		Srcs_as: *compilerAttrs.asSrcs.Clone().Append(staticAttrs.Srcs_as),
+		Copts:   *compilerAttrs.copts.Clone().Append(staticAttrs.Copts),
+		Hdrs:    *compilerAttrs.hdrs.Clone().Append(staticAttrs.Hdrs),
 
-		Copts:      compilerAttrs.copts,
+		Deps:                              *linkerAttrs.deps.Clone().Append(staticAttrs.Deps),
+		Implementation_deps:               *linkerAttrs.implementationDeps.Clone().Append(staticAttrs.Implementation_deps),
+		Dynamic_deps:                      *linkerAttrs.dynamicDeps.Clone().Append(staticAttrs.Dynamic_deps),
+		Implementation_dynamic_deps:       *linkerAttrs.implementationDynamicDeps.Clone().Append(staticAttrs.Implementation_dynamic_deps),
+		Implementation_whole_archive_deps: linkerAttrs.implementationWholeArchiveDeps,
+		Whole_archive_deps:                *linkerAttrs.wholeArchiveDeps.Clone().Append(staticAttrs.Whole_archive_deps),
+		System_dynamic_deps:               *linkerAttrs.systemDynamicDeps.Clone().Append(staticAttrs.System_dynamic_deps),
+	}
+
+	sharedCommonAttrs := staticOrSharedAttributes{
+		Srcs:    *srcs.Clone().Append(sharedAttrs.Srcs),
+		Srcs_c:  *compilerAttrs.cSrcs.Clone().Append(sharedAttrs.Srcs_c),
+		Srcs_as: *compilerAttrs.asSrcs.Clone().Append(sharedAttrs.Srcs_as),
+		Copts:   *compilerAttrs.copts.Clone().Append(sharedAttrs.Copts),
+		Hdrs:    *compilerAttrs.hdrs.Clone().Append(sharedAttrs.Hdrs),
+
+		Deps:                        *linkerAttrs.deps.Clone().Append(sharedAttrs.Deps),
+		Implementation_deps:         *linkerAttrs.implementationDeps.Clone().Append(sharedAttrs.Implementation_deps),
+		Dynamic_deps:                *linkerAttrs.dynamicDeps.Clone().Append(sharedAttrs.Dynamic_deps),
+		Implementation_dynamic_deps: *linkerAttrs.implementationDynamicDeps.Clone().Append(sharedAttrs.Implementation_dynamic_deps),
+		Whole_archive_deps:          *linkerAttrs.wholeArchiveDeps.Clone().Append(sharedAttrs.Whole_archive_deps),
+		System_dynamic_deps:         *linkerAttrs.systemDynamicDeps.Clone().Append(sharedAttrs.System_dynamic_deps),
+	}
+
+	staticTargetAttrs := &bazelCcLibraryStaticAttributes{
+		staticOrSharedAttributes: staticCommonAttrs,
+
 		Cppflags:   compilerAttrs.cppFlags,
 		Conlyflags: compilerAttrs.conlyFlags,
 		Asflags:    asFlags,
 
-		Implementation_deps:         linkerAttrs.implementationDeps,
-		Deps:                        linkerAttrs.deps,
-		Implementation_dynamic_deps: linkerAttrs.implementationDynamicDeps,
-		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,
-		Link_crt:                    linkerAttrs.linkCrt,
-		Use_libcrt:                  linkerAttrs.useLibcrt,
-		Rtti:                        compilerAttrs.rtti,
-		Stl:                         compilerAttrs.stl,
-		Cpp_std:                     compilerAttrs.cppStd,
-		C_std:                       compilerAttrs.cStd,
+		Export_includes:          exportedIncludes.Includes,
+		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+		Export_system_includes:   exportedIncludes.SystemIncludes,
+		Local_includes:           compilerAttrs.localIncludes,
+		Absolute_includes:        compilerAttrs.absoluteIncludes,
+		Use_libcrt:               linkerAttrs.useLibcrt,
+		Rtti:                     compilerAttrs.rtti,
+		Stl:                      compilerAttrs.stl,
+		Cpp_std:                  compilerAttrs.cppStd,
+		C_std:                    compilerAttrs.cStd,
+
+		Features: linkerAttrs.features,
+	}
+
+	sharedTargetAttrs := &bazelCcLibrarySharedAttributes{
+		staticOrSharedAttributes: sharedCommonAttrs,
+		Cppflags:                 compilerAttrs.cppFlags,
+		Conlyflags:               compilerAttrs.conlyFlags,
+		Asflags:                  asFlags,
+
+		Export_includes:          exportedIncludes.Includes,
+		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+		Export_system_includes:   exportedIncludes.SystemIncludes,
+		Local_includes:           compilerAttrs.localIncludes,
+		Absolute_includes:        compilerAttrs.absoluteIncludes,
+		Linkopts:                 linkerAttrs.linkopts,
+		Link_crt:                 linkerAttrs.linkCrt,
+		Use_libcrt:               linkerAttrs.useLibcrt,
+		Rtti:                     compilerAttrs.rtti,
+		Stl:                      compilerAttrs.stl,
+		Cpp_std:                  compilerAttrs.cppStd,
+		C_std:                    compilerAttrs.cStd,
 
 		Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
 
@@ -347,20 +382,42 @@
 			All:                          linkerAttrs.stripAll,
 			None:                         linkerAttrs.stripNone,
 		},
-
-		Shared: sharedAttrs,
-
-		Static: staticAttrs,
-
 		Features: linkerAttrs.features,
+
+		Stubs_symbol_file: compilerAttrs.stubsSymbolFile,
+		Stubs_versions:    compilerAttrs.stubsVersions,
 	}
 
-	props := bazel.BazelTargetModuleProperties{
-		Rule_class:        "cc_library",
-		Bzl_load_location: "//build/bazel/rules:full_cc_library.bzl",
+	for axis, configToProps := range m.GetArchVariantProperties(ctx, &LibraryProperties{}) {
+		for config, props := range configToProps {
+			if props, ok := props.(*LibraryProperties); ok {
+				if props.Inject_bssl_hash != nil {
+					// This is an edge case applies only to libcrypto
+					if m.Name() == "libcrypto" {
+						sharedTargetAttrs.Inject_bssl_hash.SetSelectValue(axis, config, props.Inject_bssl_hash)
+					} else {
+						ctx.PropertyErrorf("inject_bssl_hash", "only applies to libcrypto")
+					}
+				}
+			}
+		}
 	}
 
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+	staticProps := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_library_static",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_library_static.bzl",
+	}
+	sharedProps := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_library_shared",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_library_shared.bzl",
+	}
+
+	ctx.CreateBazelTargetModuleWithRestrictions(staticProps,
+		android.CommonAttributes{Name: m.Name() + "_bp2build_cc_library_static"},
+		staticTargetAttrs, staticAttrs.Enabled)
+	ctx.CreateBazelTargetModuleWithRestrictions(sharedProps,
+		android.CommonAttributes{Name: m.Name()},
+		sharedTargetAttrs, sharedAttrs.Enabled)
 }
 
 // cc_library creates both static and/or shared libraries for a device and/or
@@ -375,6 +432,7 @@
 		staticLibrarySdkMemberType,
 		staticAndSharedLibrarySdkMemberType,
 	}
+	module.bazelable = true
 	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
@@ -384,6 +442,7 @@
 	module, library := NewLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyStatic()
 	module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
+	module.bazelable = true
 	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
@@ -393,6 +452,7 @@
 	module, library := NewLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyShared()
 	module.sdkMemberTypes = []android.SdkMemberType{sharedLibrarySdkMemberType}
+	module.bazelable = true
 	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
@@ -403,6 +463,8 @@
 	module, library := NewLibrary(android.HostSupported)
 	library.BuildOnlyStatic()
 	module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
+	module.bazelable = true
+	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
@@ -411,6 +473,8 @@
 	module, library := NewLibrary(android.HostSupported)
 	library.BuildOnlyShared()
 	module.sdkMemberTypes = []android.SdkMemberType{sharedLibrarySdkMemberType}
+	module.bazelable = true
+	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
@@ -524,7 +588,8 @@
 	stripper         Stripper
 
 	// For whole_static_libs
-	objects Objects
+	objects                      Objects
+	wholeStaticLibsFromPrebuilts android.Paths
 
 	// Uses the module's name if empty, but can be overridden. Does not include
 	// shlib suffix.
@@ -710,9 +775,10 @@
 		if dir == "external/eigen" {
 			// Only these two directories contains exported headers.
 			for _, subdir := range []string{"Eigen", "unsupported/Eigen"} {
-				glob, err := ctx.GlobWithDeps("external/eigen/"+subdir+"/**/*", nil)
+				globDir := "external/eigen/" + subdir + "/**/*"
+				glob, err := ctx.GlobWithDeps(globDir, nil)
 				if err != nil {
-					ctx.ModuleErrorf("glob failed: %#v", err)
+					ctx.ModuleErrorf("glob of %q failed: %s", globDir, err)
 					return nil
 				}
 				for _, header := range glob {
@@ -728,9 +794,10 @@
 			}
 			continue
 		}
-		glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
+		globDir := dir + "/**/*"
+		glob, err := ctx.GlobWithDeps(globDir, nil)
 		if err != nil {
-			ctx.ModuleErrorf("glob failed: %#v", err)
+			ctx.ModuleErrorf("glob of %q failed: %s", globDir, err)
 			return nil
 		}
 		isLibcxx := strings.HasPrefix(dir, "external/libcxx/include")
@@ -1014,11 +1081,13 @@
 		srcs := android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Srcs)
 		objs = objs.Append(compileObjs(ctx, buildFlags, android.DeviceStaticLibrary, srcs,
 			android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_disabled_srcs),
+			android.PathsForModuleSrc(ctx, library.StaticProperties.Static.Tidy_timeout_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,
 			android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_disabled_srcs),
+			android.PathsForModuleSrc(ctx, library.SharedProperties.Shared.Tidy_timeout_srcs),
 			library.baseCompiler.pathDeps, library.baseCompiler.cFlagsDeps))
 	}
 
@@ -1279,6 +1348,7 @@
 
 	library.objects = deps.WholeStaticLibObjs.Copy()
 	library.objects = library.objects.Append(objs)
+	library.wholeStaticLibsFromPrebuilts = android.CopyOfPaths(deps.WholeStaticLibsFromPrebuilts)
 
 	fileName := ctx.ModuleName() + staticLibraryExtension
 	outputFile := android.PathForModuleOut(ctx, fileName)
@@ -1296,7 +1366,7 @@
 		}
 	}
 
-	transformObjToStaticLib(ctx, library.objects.objFiles, deps.WholeStaticLibsFromPrebuilts, builderFlags, outputFile, nil, objs.tidyFiles)
+	transformObjToStaticLib(ctx, library.objects.objFiles, deps.WholeStaticLibsFromPrebuilts, builderFlags, outputFile, nil, objs.tidyDepFiles)
 
 	library.coverageOutputFile = transformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
 
@@ -1304,9 +1374,10 @@
 
 	if library.static() {
 		ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
-			StaticLibrary: outputFile,
-			ReuseObjects:  library.reuseObjects,
-			Objects:       library.objects,
+			StaticLibrary:                outputFile,
+			ReuseObjects:                 library.reuseObjects,
+			Objects:                      library.objects,
+			WholeStaticLibsFromPrebuilts: library.wholeStaticLibsFromPrebuilts,
 
 			TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
 				Direct(outputFile).
@@ -1322,11 +1393,21 @@
 	return outputFile
 }
 
+func ndkSharedLibDeps(ctx ModuleContext) android.Paths {
+	if ctx.Module().(*Module).IsSdkVariant() {
+		// The NDK sysroot timestamp file depends on all the NDK
+		// sysroot header and shared library files.
+		return android.Paths{getNdkBaseTimestampFile(ctx)}
+	}
+	return nil
+}
+
 func (library *libraryDecorator) linkShared(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
 	var linkerDeps android.Paths
 	linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
+	linkerDeps = append(linkerDeps, ndkSharedLibDeps(ctx)...)
 
 	unexportedSymbols := ctx.ExpandOptionalSource(library.Properties.Unexported_symbols_list, "unexported_symbols_list")
 	forceNotWeakSymbols := ctx.ExpandOptionalSource(library.Properties.Force_symbols_not_weak_list, "force_symbols_not_weak_list")
@@ -1375,6 +1456,12 @@
 
 	builderFlags := flagsToBuilderFlags(flags)
 
+	if ctx.Darwin() && deps.DarwinSecondArchOutput.Valid() {
+		fatOutputFile := outputFile
+		outputFile = android.PathForModuleOut(ctx, "pre-fat", fileName)
+		transformDarwinUniversalBinary(ctx, fatOutputFile, outputFile, deps.DarwinSecondArchOutput.Path())
+	}
+
 	// Optimize out relinking against shared libraries whose interface hasn't changed by
 	// depending on a table of contents file instead of the library itself.
 	tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.ShlibSuffix()[1:]+".toc")
@@ -1427,7 +1514,7 @@
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
 	transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
 		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
-		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyFiles)
+		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles)
 
 	objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
 	objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -2351,30 +2438,12 @@
 	return outputFile
 }
 
-func ccSharedOrStaticBp2BuildMutator(ctx android.TopDownMutatorContext, modType string) {
-	module, ok := ctx.Module().(*Module)
-	if !ok {
-		// Not a cc module
-		return
-	}
-	if !module.ConvertWithBp2build(ctx) {
-		return
-	}
-
-	ccSharedOrStaticBp2BuildMutatorInternal(ctx, module, modType)
-}
-
-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"
-
+func sharedOrStaticLibraryBp2Build(ctx android.TopDownMutatorContext, module *Module, isStatic bool) {
 	baseAttributes := bp2BuildParseBaseProps(ctx, module)
 	compilerAttrs := baseAttributes.compilerAttributes
 	linkerAttrs := baseAttributes.linkerAttributes
 
-	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
+	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, compilerAttrs.includes)
 
 	// Append shared/static{} stanza properties. These won't be specified on
 	// cc_library_* itself, but may be specified in cc_defaults that this module
@@ -2405,28 +2474,34 @@
 		Copts:   compilerAttrs.copts,
 		Hdrs:    compilerAttrs.hdrs,
 
-		Deps:                        linkerAttrs.deps,
-		Implementation_deps:         linkerAttrs.implementationDeps,
-		Dynamic_deps:                linkerAttrs.dynamicDeps,
-		Implementation_dynamic_deps: linkerAttrs.implementationDynamicDeps,
-		Whole_archive_deps:          linkerAttrs.wholeArchiveDeps,
-		System_dynamic_deps:         linkerAttrs.systemDynamicDeps,
+		Deps:                              linkerAttrs.deps,
+		Implementation_deps:               linkerAttrs.implementationDeps,
+		Dynamic_deps:                      linkerAttrs.dynamicDeps,
+		Implementation_dynamic_deps:       linkerAttrs.implementationDynamicDeps,
+		Whole_archive_deps:                linkerAttrs.wholeArchiveDeps,
+		Implementation_whole_archive_deps: linkerAttrs.implementationWholeArchiveDeps,
+		System_dynamic_deps:               linkerAttrs.systemDynamicDeps,
 	}
 
 	var attrs interface{}
 	if isStatic {
+		commonAttrs.Deps.Add(baseAttributes.protoDependency)
 		attrs = &bazelCcLibraryStaticAttributes{
 			staticOrSharedAttributes: commonAttrs,
 
-			Use_libcrt:             linkerAttrs.useLibcrt,
-			Rtti:                   compilerAttrs.rtti,
-			Stl:                    compilerAttrs.stl,
-			Cpp_std:                compilerAttrs.cppStd,
-			C_std:                  compilerAttrs.cStd,
-			Export_includes:        exportedIncludes.Includes,
-			Export_system_includes: exportedIncludes.SystemIncludes,
-			Local_includes:         compilerAttrs.localIncludes,
-			Absolute_includes:      compilerAttrs.absoluteIncludes,
+			Use_libcrt:      linkerAttrs.useLibcrt,
+			Use_version_lib: linkerAttrs.useVersionLib,
+
+			Rtti:    compilerAttrs.rtti,
+			Stl:     compilerAttrs.stl,
+			Cpp_std: compilerAttrs.cppStd,
+			C_std:   compilerAttrs.cStd,
+
+			Export_includes:          exportedIncludes.Includes,
+			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+			Export_system_includes:   exportedIncludes.SystemIncludes,
+			Local_includes:           compilerAttrs.localIncludes,
+			Absolute_includes:        compilerAttrs.absoluteIncludes,
 
 			Cppflags:   compilerAttrs.cppFlags,
 			Conlyflags: compilerAttrs.conlyFlags,
@@ -2435,22 +2510,27 @@
 			Features: linkerAttrs.features,
 		}
 	} else {
+		commonAttrs.Dynamic_deps.Add(baseAttributes.protoDependency)
+
 		attrs = &bazelCcLibrarySharedAttributes{
 			staticOrSharedAttributes: commonAttrs,
 
 			Cppflags:   compilerAttrs.cppFlags,
 			Conlyflags: compilerAttrs.conlyFlags,
 			Asflags:    asFlags,
-			Linkopts:   linkerAttrs.linkopts,
 
-			Link_crt:   linkerAttrs.linkCrt,
-			Use_libcrt: linkerAttrs.useLibcrt,
-			Rtti:       compilerAttrs.rtti,
-			Stl:        compilerAttrs.stl,
-			Cpp_std:    compilerAttrs.cppStd,
-			C_std:      compilerAttrs.cStd,
+			Linkopts:        linkerAttrs.linkopts,
+			Link_crt:        linkerAttrs.linkCrt,
+			Use_libcrt:      linkerAttrs.useLibcrt,
+			Use_version_lib: linkerAttrs.useVersionLib,
+
+			Rtti:    compilerAttrs.rtti,
+			Stl:     compilerAttrs.stl,
+			Cpp_std: compilerAttrs.cppStd,
+			C_std:   compilerAttrs.cStd,
 
 			Export_includes:          exportedIncludes.Includes,
+			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
 			Export_system_includes:   exportedIncludes.SystemIncludes,
 			Local_includes:           compilerAttrs.localIncludes,
 			Absolute_includes:        compilerAttrs.absoluteIncludes,
@@ -2465,12 +2545,21 @@
 			},
 
 			Features: linkerAttrs.features,
+
+			Stubs_symbol_file: compilerAttrs.stubsSymbolFile,
+			Stubs_versions:    compilerAttrs.stubsVersions,
 		}
 	}
 
+	var modType string
+	if isStatic {
+		modType = "cc_library_static"
+	} else {
+		modType = "cc_library_shared"
+	}
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        modType,
-		Bzl_load_location: fmt.Sprintf("//build/bazel/rules:%s.bzl", modType),
+		Bzl_load_location: fmt.Sprintf("//build/bazel/rules/cc:%s.bzl", modType),
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
@@ -2480,17 +2569,20 @@
 type bazelCcLibraryStaticAttributes struct {
 	staticOrSharedAttributes
 
-	Use_libcrt bazel.BoolAttribute
-	Rtti       bazel.BoolAttribute
-	Stl        *string
-	Cpp_std    *string
-	C_std      *string
+	Use_libcrt      bazel.BoolAttribute
+	Use_version_lib bazel.BoolAttribute
 
-	Export_includes        bazel.StringListAttribute
-	Export_system_includes bazel.StringListAttribute
-	Local_includes         bazel.StringListAttribute
-	Absolute_includes      bazel.StringListAttribute
-	Hdrs                   bazel.LabelListAttribute
+	Rtti    bazel.BoolAttribute
+	Stl     *string
+	Cpp_std *string
+	C_std   *string
+
+	Export_includes          bazel.StringListAttribute
+	Export_absolute_includes bazel.StringListAttribute
+	Export_system_includes   bazel.StringListAttribute
+	Local_includes           bazel.StringListAttribute
+	Absolute_includes        bazel.StringListAttribute
+	Hdrs                     bazel.LabelListAttribute
 
 	Cppflags   bazel.StringListAttribute
 	Conlyflags bazel.StringListAttribute
@@ -2499,35 +2591,27 @@
 	Features bazel.StringListAttribute
 }
 
-func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
-	isLibraryStatic := ctx.ModuleType() == "cc_library_static"
-	if b, ok := ctx.Module().(android.Bazelable); ok {
-		// This is created by a custom soong config module type, so its ctx.ModuleType() is not
-		// cc_library_static. Check its BaseModuleType.
-		isLibraryStatic = isLibraryStatic || b.BaseModuleType() == "cc_library_static"
-	}
-	if isLibraryStatic {
-		ccSharedOrStaticBp2BuildMutator(ctx, "cc_library_static")
-	}
-}
-
 // TODO(b/199902614): Can this be factored to share with the other Attributes?
 type bazelCcLibrarySharedAttributes struct {
 	staticOrSharedAttributes
 
-	Linkopts   bazel.StringListAttribute
-	Link_crt   bazel.BoolAttribute // Only for linking shared library (and cc_binary)
-	Use_libcrt bazel.BoolAttribute
-	Rtti       bazel.BoolAttribute
-	Stl        *string
-	Cpp_std    *string
-	C_std      *string
+	Linkopts bazel.StringListAttribute
+	Link_crt bazel.BoolAttribute // Only for linking shared library (and cc_binary)
 
-	Export_includes        bazel.StringListAttribute
-	Export_system_includes bazel.StringListAttribute
-	Local_includes         bazel.StringListAttribute
-	Absolute_includes      bazel.StringListAttribute
-	Hdrs                   bazel.LabelListAttribute
+	Use_libcrt      bazel.BoolAttribute
+	Use_version_lib bazel.BoolAttribute
+
+	Rtti    bazel.BoolAttribute
+	Stl     *string
+	Cpp_std *string
+	C_std   *string
+
+	Export_includes          bazel.StringListAttribute
+	Export_absolute_includes bazel.StringListAttribute
+	Export_system_includes   bazel.StringListAttribute
+	Local_includes           bazel.StringListAttribute
+	Absolute_includes        bazel.StringListAttribute
+	Hdrs                     bazel.LabelListAttribute
 
 	Strip                    stripAttributes
 	Additional_linker_inputs bazel.LabelListAttribute
@@ -2537,16 +2621,8 @@
 	Asflags    bazel.StringListAttribute
 
 	Features bazel.StringListAttribute
-}
 
-func CcLibrarySharedBp2Build(ctx android.TopDownMutatorContext) {
-	isLibraryShared := ctx.ModuleType() == "cc_library_shared"
-	if b, ok := ctx.Module().(android.Bazelable); ok {
-		// This is created by a custom soong config module type, so its ctx.ModuleType() is not
-		// cc_library_shared. Check its BaseModuleType.
-		isLibraryShared = isLibraryShared || b.BaseModuleType() == "cc_library_shared"
-	}
-	if isLibraryShared {
-		ccSharedOrStaticBp2BuildMutator(ctx, "cc_library_shared")
-	}
+	Stubs_symbol_file *string
+	Stubs_versions    bazel.StringListAttribute
+	Inject_bssl_hash  bazel.BoolAttribute
 }
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 141a2a5..5d38fba 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -25,7 +25,6 @@
 	// Register sdk member types.
 	android.RegisterSdkMemberType(headersLibrarySdkMemberType)
 
-	android.RegisterBp2BuildMutator("cc_library_headers", CcLibraryHeadersBp2Build)
 }
 
 var headersLibrarySdkMemberType = &librarySdkMemberType{
@@ -96,6 +95,7 @@
 	module, library := NewLibrary(android.HostAndDeviceSupported)
 	library.HeaderOnly()
 	module.sdkMemberTypes = []android.SdkMemberType{headersLibrarySdkMemberType}
+	module.bazelable = true
 	module.bazelHandler = &libraryHeaderBazelHander{module: module, library: library}
 	return module.Init()
 }
@@ -104,49 +104,39 @@
 func prebuiltLibraryHeaderFactory() android.Module {
 	module, library := NewPrebuiltLibrary(android.HostAndDeviceSupported, "")
 	library.HeaderOnly()
+	module.bazelable = true
+	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
 type bazelCcLibraryHeadersAttributes struct {
-	Hdrs                   bazel.LabelListAttribute
-	Export_includes        bazel.StringListAttribute
-	Export_system_includes bazel.StringListAttribute
-	Deps                   bazel.LabelListAttribute
-	Implementation_deps    bazel.LabelListAttribute
-	System_dynamic_deps    bazel.LabelListAttribute
+	Hdrs                     bazel.LabelListAttribute
+	Export_includes          bazel.StringListAttribute
+	Export_absolute_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) {
-	module, ok := ctx.Module().(*Module)
-	if !ok {
-		// Not a cc module
-		return
-	}
-
-	if !module.ConvertWithBp2build(ctx) {
-		return
-	}
-
-	if ctx.ModuleType() != "cc_library_headers" {
-		return
-	}
-
-	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
+func libraryHeadersBp2Build(ctx android.TopDownMutatorContext, module *Module) {
 	baseAttributes := bp2BuildParseBaseProps(ctx, module)
+	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, baseAttributes.includes)
 	linkerAttrs := baseAttributes.linkerAttributes
 
 	attrs := &bazelCcLibraryHeadersAttributes{
-		Export_includes:        exportedIncludes.Includes,
-		Export_system_includes: exportedIncludes.SystemIncludes,
-		Implementation_deps:    linkerAttrs.implementationDeps,
-		Deps:                   linkerAttrs.deps,
-		System_dynamic_deps:    linkerAttrs.systemDynamicDeps,
-		Hdrs:                   baseAttributes.hdrs,
+		Export_includes:          exportedIncludes.Includes,
+		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+		Export_system_includes:   exportedIncludes.SystemIncludes,
+		Implementation_deps:      linkerAttrs.implementationDeps,
+		Deps:                     linkerAttrs.deps,
+		System_dynamic_deps:      linkerAttrs.systemDynamicDeps,
+		Hdrs:                     baseAttributes.hdrs,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_library_headers",
-		Bzl_load_location: "//build/bazel/rules:cc_library_headers.bzl",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_library_headers.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
diff --git a/cc/library_test.go b/cc/library_test.go
index 7ddfaa7..6d5eda2 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -257,9 +257,16 @@
 				CcObjectFiles:        []string{"foo.o"},
 				Includes:             []string{"include"},
 				SystemIncludes:       []string{"system_include"},
-				RootStaticArchives:   []string{"foo.a"},
+				Headers:              []string{"foo.h"},
 				RootDynamicLibraries: []string{"foo.so"},
 			},
+			"//foo/bar:bar_bp2build_cc_library_static": cquery.CcInfo{
+				CcObjectFiles:      []string{"foo.o"},
+				Includes:           []string{"include"},
+				SystemIncludes:     []string{"system_include"},
+				Headers:            []string{"foo.h"},
+				RootStaticArchives: []string{"foo.a"},
+			},
 		},
 	}
 	ctx := testCcWithConfig(t, config)
@@ -273,18 +280,25 @@
 	expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.a"}
 	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
 
+	flagExporter := ctx.ModuleProvider(staticFoo, FlagExporterInfoProvider).(FlagExporterInfo)
+	android.AssertPathsRelativeToTopEquals(t, "exported include dirs", []string{"outputbase/execroot/__main__/include"}, flagExporter.IncludeDirs)
+	android.AssertPathsRelativeToTopEquals(t, "exported system include dirs", []string{"outputbase/execroot/__main__/system_include"}, flagExporter.SystemIncludeDirs)
+	android.AssertPathsRelativeToTopEquals(t, "exported headers", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.GeneratedHeaders)
+	android.AssertPathsRelativeToTopEquals(t, "deps", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.Deps)
+
 	sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
 	outputFiles, err = sharedFoo.(android.OutputFileProducer).OutputFiles("")
 	if err != nil {
-		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+		t.Errorf("Unexpected error getting cc_library outputfiles %s", err)
 	}
 	expectedOutputFiles = []string{"outputbase/execroot/__main__/foo.so"}
 	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
 
-	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)
+	flagExporter = ctx.ModuleProvider(sharedFoo, FlagExporterInfoProvider).(FlagExporterInfo)
+	android.AssertPathsRelativeToTopEquals(t, "exported include dirs", []string{"outputbase/execroot/__main__/include"}, flagExporter.IncludeDirs)
+	android.AssertPathsRelativeToTopEquals(t, "exported system include dirs", []string{"outputbase/execroot/__main__/system_include"}, flagExporter.SystemIncludeDirs)
+	android.AssertPathsRelativeToTopEquals(t, "exported headers", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.GeneratedHeaders)
+	android.AssertPathsRelativeToTopEquals(t, "deps", []string{"outputbase/execroot/__main__/foo.h"}, flagExporter.Deps)
 }
 
 func TestLibraryVersionScript(t *testing.T) {
@@ -365,3 +379,77 @@
 	gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
 	android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
 }
+
+func TestWholeStaticLibPrebuilts(t *testing.T) {
+	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+		cc_prebuilt_library_static {
+			name: "libprebuilt",
+			srcs: ["foo.a"],
+		}
+
+		cc_library_static {
+			name: "libdirect",
+			whole_static_libs: ["libprebuilt"],
+		}
+
+		cc_library_static {
+			name: "libtransitive",
+			whole_static_libs: ["libdirect"],
+		}
+
+		cc_library_static {
+			name: "libdirect_with_srcs",
+			srcs: ["bar.c"],
+			whole_static_libs: ["libprebuilt"],
+		}
+
+		cc_library_static {
+			name: "libtransitive_with_srcs",
+			srcs: ["baz.c"],
+			whole_static_libs: ["libdirect_with_srcs"],
+		}
+	`)
+
+	libdirect := result.ModuleForTests("libdirect", "android_arm64_armv8-a_static").Rule("arWithLibs")
+	libtransitive := result.ModuleForTests("libtransitive", "android_arm64_armv8-a_static").Rule("arWithLibs")
+
+	libdirectWithSrcs := result.ModuleForTests("libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
+	libtransitiveWithSrcs := result.ModuleForTests("libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("arWithLibs")
+
+	barObj := result.ModuleForTests("libdirect_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
+	bazObj := result.ModuleForTests("libtransitive_with_srcs", "android_arm64_armv8-a_static").Rule("cc")
+
+	android.AssertStringListContains(t, "missing dependency on foo.a",
+		libdirect.Inputs.Strings(), "foo.a")
+	android.AssertStringDoesContain(t, "missing flag for foo.a",
+		libdirect.Args["arLibs"], "foo.a")
+
+	android.AssertStringListContains(t, "missing dependency on foo.a",
+		libtransitive.Inputs.Strings(), "foo.a")
+	android.AssertStringDoesContain(t, "missing flag for foo.a",
+		libtransitive.Args["arLibs"], "foo.a")
+
+	android.AssertStringListContains(t, "missing dependency on foo.a",
+		libdirectWithSrcs.Inputs.Strings(), "foo.a")
+	android.AssertStringDoesContain(t, "missing flag for foo.a",
+		libdirectWithSrcs.Args["arLibs"], "foo.a")
+	android.AssertStringListContains(t, "missing dependency on bar.o",
+		libdirectWithSrcs.Inputs.Strings(), barObj.Output.String())
+	android.AssertStringDoesContain(t, "missing flag for bar.o",
+		libdirectWithSrcs.Args["arObjs"], barObj.Output.String())
+
+	android.AssertStringListContains(t, "missing dependency on foo.a",
+		libtransitiveWithSrcs.Inputs.Strings(), "foo.a")
+	android.AssertStringDoesContain(t, "missing flag for foo.a",
+		libtransitiveWithSrcs.Args["arLibs"], "foo.a")
+
+	android.AssertStringListContains(t, "missing dependency on bar.o",
+		libtransitiveWithSrcs.Inputs.Strings(), barObj.Output.String())
+	android.AssertStringDoesContain(t, "missing flag for bar.o",
+		libtransitiveWithSrcs.Args["arObjs"], barObj.Output.String())
+
+	android.AssertStringListContains(t, "missing dependency on baz.o",
+		libtransitiveWithSrcs.Inputs.Strings(), bazObj.Output.String())
+	android.AssertStringDoesContain(t, "missing flag for baz.o",
+		libtransitiveWithSrcs.Args["arObjs"], bazObj.Output.String())
+}
diff --git a/cc/linkable.go b/cc/linkable.go
index 560c9de..d4b4770 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -352,6 +352,11 @@
 	Objects       Objects
 	ReuseObjects  Objects
 
+	// A static library may contain prebuilt static libraries included with whole_static_libs
+	// that won't appear in Objects.  They are transitively available in
+	// WholeStaticLibsFromPrebuilts.
+	WholeStaticLibsFromPrebuilts android.Paths
+
 	// This isn't the actual transitive DepSet, shared library dependencies have been
 	// converted into static library analogues.  It is only used to order the static
 	// library dependencies that were specified for the current module.
@@ -384,9 +389,13 @@
 
 	includes := android.PathsForBazelOut(ctx, ccInfo.Includes)
 	systemIncludes := android.PathsForBazelOut(ctx, ccInfo.SystemIncludes)
+	headers := android.PathsForBazelOut(ctx, ccInfo.Headers)
 
 	return FlagExporterInfo{
 		IncludeDirs:       android.FirstUniquePaths(includes),
 		SystemIncludeDirs: android.FirstUniquePaths(systemIncludes),
+		GeneratedHeaders:  headers,
+		// necessary to ensure generated headers are considered implicit deps of dependent actions
+		Deps: headers,
 	}
 }
diff --git a/cc/linker.go b/cc/linker.go
index aaaca7a..bea65d4 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -427,7 +427,7 @@
 
 	deps.LateSharedLibs = append(deps.LateSharedLibs, deps.SystemSharedLibs...)
 
-	if ctx.Windows() {
+	if ctx.Windows() && ctx.ModuleName() != "libwinpthread" {
 		deps.LateStaticLibs = append(deps.LateStaticLibs, "libwinpthread")
 	}
 
diff --git a/cc/lto.go b/cc/lto.go
index 6d55579..2c274bd 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -123,7 +123,7 @@
 
 		// If the module does not have a profile, be conservative and limit cross TU inline
 		// limit to 5 LLVM IR instructions, to balance binary size increase and performance.
-		if !ctx.isPgoCompile() {
+		if !ctx.isPgoCompile() && !ctx.isAfdoCompile() {
 			flags.Local.LdFlags = append(flags.Local.LdFlags,
 				"-Wl,-plugin-opt,-import-instr-limit=5")
 		}
diff --git a/cc/makevars.go b/cc/makevars.go
index 8d7a163..6752f8c 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -95,6 +95,7 @@
 	ctx.Strict("CLANG_EXTERNAL_CFLAGS", "${config.ExternalCflags}")
 	ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.NoOverrideGlobalCflags}")
 	ctx.Strict("GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE", "")
+	ctx.Strict("GLOBAL_CLANG_EXTERNAL_CFLAGS_NO_OVERRIDE", "${config.NoOverrideExternalGlobalCflags}")
 
 	ctx.Strict("BOARD_VNDK_VERSION", ctx.DeviceConfig().VndkVersion())
 	ctx.Strict("RECOVERY_SNAPSHOT_VERSION", ctx.DeviceConfig().RecoverySnapshotVersion())
@@ -226,7 +227,6 @@
 	}
 
 	clangPrefix := secondPrefix + "CLANG_" + typePrefix
-	clangExtras := "-B" + config.ToolPath(toolchain)
 
 	ctx.Strict(clangPrefix+"TRIPLE", toolchain.ClangTriple())
 	ctx.Strict(clangPrefix+"GLOBAL_CFLAGS", strings.Join([]string{
@@ -234,7 +234,6 @@
 		"${config.CommonGlobalCflags}",
 		fmt.Sprintf("${config.%sGlobalCflags}", hod),
 		toolchain.ToolchainCflags(),
-		clangExtras,
 		productExtraCflags,
 	}, " "))
 	ctx.Strict(clangPrefix+"GLOBAL_CPPFLAGS", strings.Join([]string{
@@ -247,25 +246,52 @@
 		toolchain.Ldflags(),
 		toolchain.ToolchainLdflags(),
 		productExtraLdflags,
-		clangExtras,
 	}, " "))
 	ctx.Strict(clangPrefix+"GLOBAL_LLDFLAGS", strings.Join([]string{
 		fmt.Sprintf("${config.%sGlobalLldflags}", hod),
 		toolchain.Lldflags(),
 		toolchain.ToolchainLdflags(),
 		productExtraLdflags,
-		clangExtras,
 	}, " "))
 
 	if target.Os.Class == android.Device {
-		ctx.Strict(secondPrefix+"ADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.AddressSanitizerRuntimeLibrary(toolchain), ".so"))
-		ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_RUNTIME_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerRuntimeLibrary(toolchain), ".so"))
-		ctx.Strict(secondPrefix+"HWADDRESS_SANITIZER_STATIC_LIBRARY", strings.TrimSuffix(config.HWAddressSanitizerStaticLibrary(toolchain), ".a"))
-		ctx.Strict(secondPrefix+"UBSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain), ".so"))
-		ctx.Strict(secondPrefix+"UBSAN_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain), ".a"))
-		ctx.Strict(secondPrefix+"TSAN_RUNTIME_LIBRARY", strings.TrimSuffix(config.ThreadSanitizerRuntimeLibrary(toolchain), ".so"))
-		ctx.Strict(secondPrefix+"SCUDO_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoRuntimeLibrary(toolchain), ".so"))
-		ctx.Strict(secondPrefix+"SCUDO_MINIMAL_RUNTIME_LIBRARY", strings.TrimSuffix(config.ScudoMinimalRuntimeLibrary(toolchain), ".so"))
+		sanitizerVariables := map[string]string{
+			"ADDRESS_SANITIZER_RUNTIME_LIBRARY":   config.AddressSanitizerRuntimeLibrary(toolchain),
+			"HWADDRESS_SANITIZER_RUNTIME_LIBRARY": config.HWAddressSanitizerRuntimeLibrary(toolchain),
+			"HWADDRESS_SANITIZER_STATIC_LIBRARY":  config.HWAddressSanitizerStaticLibrary(toolchain),
+			"UBSAN_RUNTIME_LIBRARY":               config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain),
+			"UBSAN_MINIMAL_RUNTIME_LIBRARY":       config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain),
+			"TSAN_RUNTIME_LIBRARY":                config.ThreadSanitizerRuntimeLibrary(toolchain),
+			"SCUDO_RUNTIME_LIBRARY":               config.ScudoRuntimeLibrary(toolchain),
+			"SCUDO_MINIMAL_RUNTIME_LIBRARY":       config.ScudoMinimalRuntimeLibrary(toolchain),
+		}
+
+		for variable, value := range sanitizerVariables {
+			ctx.Strict(secondPrefix+variable, value)
+		}
+
+		sanitizerLibs := android.SortedStringValues(sanitizerVariables)
+		var sanitizerLibStems []string
+		ctx.VisitAllModules(func(m android.Module) {
+			if !m.Enabled() {
+				return
+			}
+
+			ccModule, _ := m.(*Module)
+			if ccModule == nil || ccModule.library == nil || !ccModule.library.shared() {
+				return
+			}
+
+			if android.InList(strings.TrimPrefix(ctx.ModuleName(m), "prebuilt_"), sanitizerLibs) &&
+				m.Target().Os == target.Os && m.Target().Arch.ArchType == target.Arch.ArchType {
+				outputFile := ccModule.outputFile
+				if outputFile.Valid() {
+					sanitizerLibStems = append(sanitizerLibStems, outputFile.Path().Base())
+				}
+			}
+		})
+		sanitizerLibStems = android.SortedUniqueStrings(sanitizerLibStems)
+		ctx.Strict(secondPrefix+"SANITIZER_STEMS", strings.Join(sanitizerLibStems, " "))
 	}
 
 	// This is used by external/gentoo/...
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 7879a7d..fc682ff 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -25,6 +25,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/cc/config"
 )
 
 func init() {
@@ -208,20 +209,26 @@
 	*ndkKnownLibs = append(*ndkKnownLibs, name)
 }
 
+var stubLibraryCompilerFlags = []string{
+	// We're knowingly doing some otherwise unsightly things with builtin
+	// functions here. We're just generating stub libraries, so ignore it.
+	"-Wno-incompatible-library-redeclaration",
+	"-Wno-incomplete-setjmp-declaration",
+	"-Wno-builtin-requires-header",
+	"-Wno-invalid-noreturn",
+	"-Wall",
+	"-Werror",
+	// These libraries aren't actually used. Don't worry about unwinding
+	// (avoids the need to link an unwinder into a fake library).
+	"-fno-unwind-tables",
+}
+
+func init() {
+	config.ExportStringList("StubLibraryCompilerFlags", stubLibraryCompilerFlags)
+}
+
 func addStubLibraryCompilerFlags(flags Flags) Flags {
-	flags.Global.CFlags = append(flags.Global.CFlags,
-		// We're knowingly doing some otherwise unsightly things with builtin
-		// functions here. We're just generating stub libraries, so ignore it.
-		"-Wno-incompatible-library-redeclaration",
-		"-Wno-incomplete-setjmp-declaration",
-		"-Wno-builtin-requires-header",
-		"-Wno-invalid-noreturn",
-		"-Wall",
-		"-Werror",
-		// These libraries aren't actually used. Don't worry about unwinding
-		// (avoids the need to link an unwinder into a fake library).
-		"-fno-unwind-tables",
-	)
+	flags.Global.CFlags = append(flags.Global.CFlags, stubLibraryCompilerFlags...)
 	// All symbols in the stubs library should be visible.
 	if inList("-fvisibility=hidden", flags.Local.CFlags) {
 		flags.Local.CFlags = append(flags.Local.CFlags, "-fvisibility=default")
@@ -272,7 +279,7 @@
 
 func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects {
 	return compileObjs(ctx, flagsToBuilderFlags(flags), "",
-		android.Paths{src}, nil, nil, nil)
+		android.Paths{src}, nil, nil, nil, nil)
 }
 
 func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path {
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index fd458d9..6c200f5 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -82,6 +82,12 @@
 	return android.PathForOutput(ctx, "ndk_base.timestamp")
 }
 
+// The headers timestamp file depends only on the NDK headers.
+// This is used mainly for .tidy files that do not need any stub libraries.
+func getNdkHeadersTimestampFile(ctx android.PathContext) android.WritablePath {
+	return android.PathForOutput(ctx, "ndk_headers.timestamp")
+}
+
 // The full timestamp file depends on the base timestamp *and* the static
 // libraries.
 func getNdkFullTimestampFile(ctx android.PathContext) android.WritablePath {
@@ -96,6 +102,7 @@
 
 func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 	var staticLibInstallPaths android.Paths
+	var headerPaths android.Paths
 	var installPaths android.Paths
 	var licensePaths android.Paths
 	ctx.VisitAllModules(func(module android.Module) {
@@ -104,16 +111,19 @@
 		}
 
 		if m, ok := module.(*headerModule); ok {
+			headerPaths = append(headerPaths, m.installPaths...)
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
 
 		if m, ok := module.(*versionedHeaderModule); ok {
+			headerPaths = append(headerPaths, m.installPaths...)
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
 
 		if m, ok := module.(*preprocessedHeadersModule); ok {
+			headerPaths = append(headerPaths, m.installPaths...)
 			installPaths = append(installPaths, m.installPaths...)
 			licensePaths = append(licensePaths, m.licensePath)
 		}
@@ -153,6 +163,12 @@
 		Validation: getNdkAbiDiffTimestampFile(ctx),
 	})
 
+	ctx.Build(pctx, android.BuildParams{
+		Rule:      android.Touch,
+		Output:    getNdkHeadersTimestampFile(ctx),
+		Implicits: headerPaths,
+	})
+
 	fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
 
 	// There's a phony "ndk" rule defined in core/main.mk that depends on this.
diff --git a/cc/object.go b/cc/object.go
index 0327a45..fdd0b11 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -29,7 +29,6 @@
 	android.RegisterModuleType("cc_object", ObjectFactory)
 	android.RegisterSdkMemberType(ccObjectSdkMemberType)
 
-	android.RegisterBp2BuildMutator("cc_object", ObjectBp2Build)
 }
 
 var ccObjectSdkMemberType = &librarySdkMemberType{
@@ -117,6 +116,7 @@
 
 	module.sdkMemberTypes = []android.SdkMemberType{ccObjectSdkMemberType}
 
+	module.bazelable = true
 	return module.Init()
 }
 
@@ -135,19 +135,9 @@
 	Linker_script       bazel.LabelAttribute
 }
 
-// ObjectBp2Build is the bp2build converter from cc_object modules to the
+// objectBp2Build is the bp2build converter from cc_object modules to the
 // Bazel equivalent target, plus any necessary include deps for the cc_object.
-func ObjectBp2Build(ctx android.TopDownMutatorContext) {
-	m, ok := ctx.Module().(*Module)
-	if !ok || !m.ConvertWithBp2build(ctx) {
-		return
-	}
-
-	// a Module can be something other than a cc_object.
-	if ctx.ModuleType() != "cc_object" {
-		return
-	}
-
+func objectBp2Build(ctx android.TopDownMutatorContext, m *Module) {
 	if m.compiler == nil {
 		// a cc_object must have access to the compiler decorator for its props.
 		ctx.ModuleErrorf("compiler must not be nil for a cc_object module")
@@ -165,7 +155,8 @@
 		for config, props := range configToProps {
 			if objectLinkerProps, ok := props.(*ObjectLinkerProperties); ok {
 				if objectLinkerProps.Linker_script != nil {
-					linkerScript.SetSelectValue(axis, config, android.BazelLabelForModuleSrcSingle(ctx, *objectLinkerProps.Linker_script))
+					label := android.BazelLabelForModuleSrcSingle(ctx, *objectLinkerProps.Linker_script)
+					linkerScript.SetSelectValue(axis, config, label)
 				}
 				deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs))
 				systemSharedLibs := objectLinkerProps.System_shared_libs
@@ -204,7 +195,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_object",
-		Bzl_load_location: "//build/bazel/rules:cc_object.bzl",
+		Bzl_load_location: "//build/bazel/rules/cc:cc_object.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
diff --git a/cc/pgo.go b/cc/pgo.go
index e78549e..aa0feae 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -96,10 +96,6 @@
 	flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrumentFlag)
 	return flags
 }
-func (props *PgoProperties) addSamplingProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
-	flags.Local.CFlags = append(flags.Local.CFlags, props.Pgo.Cflags...)
-	return flags
-}
 
 func (props *PgoProperties) getPgoProfileFile(ctx BaseModuleContext) android.OptionalPath {
 	profileFile := *props.Pgo.Profile_file
@@ -212,6 +208,10 @@
 		ctx.ModuleErrorf("Instrumentation PGO specification is missing benchmark property")
 	}
 
+	if isSampling {
+		ctx.ModuleErrorf("Sampling PGO is deprecated, use AFDO instead")
+	}
+
 	if isSampling && isInstrumentation {
 		ctx.PropertyErrorf("pgo", "Exactly one of \"instrumentation\" and \"sampling\" properties must be set")
 	}
@@ -313,10 +313,6 @@
 	if (props.ShouldProfileModule && props.isInstrumentation()) || props.PgoInstrLink {
 		// Instrumentation PGO use and gather flags cannot coexist.
 		return props.addInstrumentationProfileGatherFlags(ctx, flags)
-	} else if props.ShouldProfileModule && props.isSampling() {
-		flags = props.addSamplingProfileGatherFlags(ctx, flags)
-	} else if ctx.DeviceConfig().SamplingPGO() {
-		flags = props.addSamplingProfileGatherFlags(ctx, flags)
 	}
 
 	if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") {
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index c303fda..339a16d 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -32,8 +32,6 @@
 	ctx.RegisterModuleType("cc_prebuilt_test_library_shared", PrebuiltSharedTestLibraryFactory)
 	ctx.RegisterModuleType("cc_prebuilt_object", prebuiltObjectFactory)
 	ctx.RegisterModuleType("cc_prebuilt_binary", prebuiltBinaryFactory)
-
-	android.RegisterBp2BuildMutator("cc_prebuilt_library_shared", PrebuiltLibrarySharedBp2Build)
 }
 
 type prebuiltLinkerInterface interface {
@@ -299,6 +297,8 @@
 func NewPrebuiltSharedLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
 	module, library := NewPrebuiltLibrary(hod, "srcs")
 	library.BuildOnlyShared()
+	module.bazelable = true
+	module.bazelHandler = &prebuiltSharedLibraryBazelHandler{module: module, library: library}
 
 	// Prebuilt shared libraries can be included in APEXes
 	android.InitApexModule(module)
@@ -316,31 +316,41 @@
 func NewPrebuiltStaticLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
 	module, library := NewPrebuiltLibrary(hod, "srcs")
 	library.BuildOnlyStatic()
+	module.bazelable = true
 	module.bazelHandler = &prebuiltStaticLibraryBazelHandler{module: module, library: library}
 	return module, library
 }
 
+type bazelPrebuiltLibraryStaticAttributes struct {
+	Static_library         bazel.LabelAttribute
+	Export_includes        bazel.StringListAttribute
+	Export_system_includes bazel.StringListAttribute
+}
+
+func prebuiltLibraryStaticBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+	prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module)
+	exportedIncludes := Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx, module)
+
+	attrs := &bazelPrebuiltLibraryStaticAttributes{
+		Static_library:         prebuiltAttrs.Src,
+		Export_includes:        exportedIncludes.Includes,
+		Export_system_includes: exportedIncludes.SystemIncludes,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "prebuilt_library_static",
+		Bzl_load_location: "//build/bazel/rules/cc:prebuilt_library_static.bzl",
+	}
+
+	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
+}
+
 type bazelPrebuiltLibrarySharedAttributes struct {
 	Shared_library bazel.LabelAttribute
 }
 
-func PrebuiltLibrarySharedBp2Build(ctx android.TopDownMutatorContext) {
-	module, ok := ctx.Module().(*Module)
-	if !ok {
-		// Not a cc module
-		return
-	}
-	if !module.ConvertWithBp2build(ctx) {
-		return
-	}
-	if ctx.ModuleType() != "cc_prebuilt_library_shared" {
-		return
-	}
-
-	prebuiltLibrarySharedBp2BuildInternal(ctx, module)
-}
-
-func prebuiltLibrarySharedBp2BuildInternal(ctx android.TopDownMutatorContext, module *Module) {
+func prebuiltLibrarySharedBp2Build(ctx android.TopDownMutatorContext, module *Module) {
 	prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module)
 
 	attrs := &bazelPrebuiltLibrarySharedAttributes{
@@ -349,7 +359,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "prebuilt_library_shared",
-		Bzl_load_location: "//build/bazel/rules:prebuilt_library_shared.bzl",
+		Bzl_load_location: "//build/bazel/rules/cc:prebuilt_library_shared.bzl",
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
@@ -417,6 +427,69 @@
 	return true
 }
 
+type prebuiltSharedLibraryBazelHandler struct {
+	android.BazelHandler
+
+	module  *Module
+	library *libraryDecorator
+}
+
+func (h *prebuiltSharedLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+	bazelCtx := ctx.Config().BazelContext
+	ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
+	if err != nil {
+		ctx.ModuleErrorf("Error getting Bazel CcInfo for %s: %s", label, err)
+	}
+	if !ok {
+		return false
+	}
+	sharedLibs := ccInfo.CcSharedLibraryFiles
+	if len(sharedLibs) != 1 {
+		ctx.ModuleErrorf("expected 1 shared library from bazel target %s, got %q", label, sharedLibs)
+		return false
+	}
+
+	// TODO(b/184543518): cc_prebuilt_library_shared may have properties for re-exporting flags
+
+	// TODO(eakammer):Add stub-related flags if this library is a stub library.
+	// h.library.exportVersioningMacroIfNeeded(ctx)
+
+	// Dependencies on this library will expect collectedSnapshotHeaders to be set, otherwise
+	// validation will fail. For now, set this to an empty list.
+	// TODO(cparsons): More closely mirror the collectHeadersForSnapshot implementation.
+	h.library.collectedSnapshotHeaders = android.Paths{}
+
+	if len(sharedLibs) == 0 {
+		h.module.outputFile = android.OptionalPath{}
+		return true
+	}
+
+	out := android.PathForBazelOut(ctx, sharedLibs[0])
+	h.module.outputFile = android.OptionalPathForPath(out)
+
+	// FIXME(b/214600441): We don't yet strip prebuilt shared libraries
+	h.library.unstrippedOutputFile = out
+
+	var toc android.Path
+	if len(ccInfo.TocFile) > 0 {
+		toc = android.PathForBazelOut(ctx, ccInfo.TocFile)
+	} else {
+		toc = out // Just reuse `out` so ninja still gets an input but won't matter
+	}
+
+	info := SharedLibraryInfo{
+		SharedLibrary:   out,
+		TableOfContents: android.OptionalPathForPath(toc),
+		Target:          ctx.Target(),
+	}
+	ctx.SetProvider(SharedLibraryInfoProvider, info)
+
+	h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo)
+	h.module.maybeUnhideFromMake()
+
+	return true
+}
+
 func (p *prebuiltObjectLinker) prebuilt() *android.Prebuilt {
 	return &p.Prebuilt
 }
@@ -543,7 +616,7 @@
 }
 
 func NewPrebuiltBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
-	module, binary := NewBinary(hod)
+	module, binary := newBinary(hod, false)
 	module.compiler = nil
 
 	prebuilt := &prebuiltBinaryLinker{
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index c8f8103..94f75fe 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -19,7 +19,7 @@
 	"testing"
 
 	"android/soong/android"
-
+	"android/soong/bazel/cquery"
 	"github.com/google/blueprint"
 )
 
@@ -378,3 +378,74 @@
 	static2 = ctx.ModuleForTests("libtest_static", "android_arm64_armv8-a_static_hwasan").Module().(*Module)
 	assertString(t, static2.OutputFile().Path().Base(), "libf.hwasan.a")
 }
+
+func TestPrebuiltLibrarySharedWithBazelWithoutToc(t *testing.T) {
+	const bp = `
+cc_prebuilt_library_shared {
+	name: "foo",
+	srcs: ["foo.so"],
+	bazel_module: { label: "//foo/bar:bar" },
+}`
+	outBaseDir := "outputbase"
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.BazelContext = android.MockBazelContext{
+		OutputBaseDir: outBaseDir,
+		LabelToCcInfo: map[string]cquery.CcInfo{
+			"//foo/bar:bar": cquery.CcInfo{
+				CcSharedLibraryFiles: []string{"foo.so"},
+			},
+		},
+	}
+	ctx := testCcWithConfig(t, config)
+	sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+	pathPrefix := outBaseDir + "/execroot/__main__/"
+
+	info := ctx.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo)
+	android.AssertPathRelativeToTopEquals(t, "prebuilt shared library",
+		pathPrefix+"foo.so", info.SharedLibrary)
+	android.AssertPathRelativeToTopEquals(t, "prebuilt's 'nullary' ToC",
+		pathPrefix+"foo.so", info.TableOfContents.Path())
+
+	outputFiles, err := sharedFoo.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{pathPrefix + "foo.so"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+}
+
+func TestPrebuiltLibrarySharedWithBazelWithToc(t *testing.T) {
+	const bp = `
+cc_prebuilt_library_shared {
+	name: "foo",
+	srcs: ["foo.so"],
+	bazel_module: { label: "//foo/bar:bar" },
+}`
+	outBaseDir := "outputbase"
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.BazelContext = android.MockBazelContext{
+		OutputBaseDir: outBaseDir,
+		LabelToCcInfo: map[string]cquery.CcInfo{
+			"//foo/bar:bar": cquery.CcInfo{
+				CcSharedLibraryFiles: []string{"foo.so"},
+				TocFile:              "toc",
+			},
+		},
+	}
+	ctx := testCcWithConfig(t, config)
+	sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+	pathPrefix := outBaseDir + "/execroot/__main__/"
+
+	info := ctx.ModuleProvider(sharedFoo, SharedLibraryInfoProvider).(SharedLibraryInfo)
+	android.AssertPathRelativeToTopEquals(t, "prebuilt shared library's ToC",
+		pathPrefix+"toc", info.TableOfContents.Path())
+	android.AssertPathRelativeToTopEquals(t, "prebuilt shared library",
+		pathPrefix+"foo.so", info.SharedLibrary)
+
+	outputFiles, err := sharedFoo.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+	expectedOutputFiles := []string{pathPrefix + "foo.so"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+}
diff --git a/cc/proto.go b/cc/proto.go
index 4466144..3cf1453 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -16,8 +16,14 @@
 
 import (
 	"github.com/google/blueprint/pathtools"
+	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
+)
+
+const (
+	protoTypeDefault = "lite"
 )
 
 // genProto creates a rule to convert a .proto file to generated .pb.cc and .pb.h files and returns
@@ -63,7 +69,7 @@
 	var lib string
 
 	if String(p.Proto.Plugin) == "" {
-		switch String(p.Proto.Type) {
+		switch proptools.StringDefault(p.Proto.Type, protoTypeDefault) {
 		case "full":
 			if ctx.useSdk() {
 				lib = "libprotobuf-cpp-full-ndk"
@@ -71,7 +77,7 @@
 			} else {
 				lib = "libprotobuf-cpp-full"
 			}
-		case "lite", "":
+		case "lite":
 			if ctx.useSdk() {
 				lib = "libprotobuf-cpp-lite-ndk"
 				static = true
@@ -157,3 +163,69 @@
 
 	return flags
 }
+
+type protoAttributes struct {
+	Deps bazel.LabelListAttribute
+}
+
+type bp2buildProtoDeps struct {
+	wholeStaticLib               *bazel.LabelAttribute
+	implementationWholeStaticLib *bazel.LabelAttribute
+	protoDep                     *bazel.LabelAttribute
+}
+
+func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) bp2buildProtoDeps {
+	var ret bp2buildProtoDeps
+
+	protoInfo, ok := android.Bp2buildProtoProperties(ctx, m, protoSrcs)
+	if !ok {
+		return ret
+	}
+
+	var depName string
+	typ := proptools.StringDefault(protoInfo.Type, protoTypeDefault)
+	var rule_class string
+	suffix := "_cc_proto"
+	switch typ {
+	case "lite":
+		suffix += "_lite"
+		rule_class = "cc_lite_proto_library"
+		depName = "libprotobuf-cpp-lite"
+	case "full":
+		rule_class = "cc_proto_library"
+		depName = "libprotobuf-cpp-full"
+	default:
+		ctx.PropertyErrorf("proto.type", "cannot handle conversion at this time: %q", typ)
+	}
+
+	dep := android.BazelLabelForModuleDepSingle(ctx, depName)
+	ret.protoDep = &bazel.LabelAttribute{Value: &dep}
+
+	protoLabel := bazel.Label{Label: ":" + protoInfo.Name}
+	var protoAttrs protoAttributes
+	protoAttrs.Deps.SetValue(bazel.LabelList{Includes: []bazel.Label{protoLabel}})
+
+	name := m.Name() + suffix
+
+	ctx.CreateBazelTargetModule(
+		bazel.BazelTargetModuleProperties{
+			Rule_class:        rule_class,
+			Bzl_load_location: "//build/bazel/rules/cc:cc_proto.bzl",
+		},
+		android.CommonAttributes{Name: name},
+		&protoAttrs)
+
+	var privateHdrs bool
+	if lib, ok := m.linker.(*libraryDecorator); ok {
+		privateHdrs = !proptools.Bool(lib.Properties.Proto.Export_proto_headers)
+	}
+
+	labelAttr := &bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + name}}
+	if privateHdrs {
+		ret.implementationWholeStaticLib = labelAttr
+	} else {
+		ret.wholeStaticLib = labelAttr
+	}
+
+	return ret
+}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 93d4b4c..f8661a6 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -35,7 +35,6 @@
 
 	asanCflags = []string{
 		"-fno-omit-frame-pointer",
-		"-fno-experimental-new-pass-manager",
 	}
 	asanLdflags = []string{"-Wl,-u,__asan_preinit"}
 
@@ -43,6 +42,7 @@
 		"-fno-omit-frame-pointer",
 		"-Wno-frame-larger-than=",
 		"-fsanitize-hwaddress-abi=platform",
+		"-mllvm", "-hwasan-use-after-scope=1",
 	}
 
 	// ThinLTO performs codegen during link time, thus these flags need to
@@ -540,11 +540,6 @@
 	if Bool(s.Hwaddress) {
 		s.Address = nil
 		s.Thread = nil
-		// Disable ubsan diagnosic as a workaround for a compiler bug.
-		// TODO(b/191808836): re-enable.
-		s.Diag.Undefined = nil
-		s.Diag.Integer_overflow = nil
-		s.Diag.Misc_undefined = nil
 	}
 
 	// TODO(b/131771163): CFI transiently depends on LTO, and thus Fuzzer is
@@ -588,20 +583,12 @@
 
 func (sanitize *sanitize) flags(ctx ModuleContext, flags Flags) Flags {
 	minimalRuntimeLib := config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(ctx.toolchain()) + ".a"
-	minimalRuntimePath := "${config.ClangAsanLibDir}/" + minimalRuntimeLib
-	builtinsRuntimeLib := config.BuiltinsRuntimeLibrary(ctx.toolchain()) + ".a"
-	builtinsRuntimePath := "${config.ClangAsanLibDir}/" + builtinsRuntimeLib
 
 	if sanitize.Properties.MinimalRuntimeDep {
 		flags.Local.LdFlags = append(flags.Local.LdFlags,
-			minimalRuntimePath,
 			"-Wl,--exclude-libs,"+minimalRuntimeLib)
 	}
 
-	if sanitize.Properties.BuiltinsDep {
-		flags.libFlags = append([]string{builtinsRuntimePath}, flags.libFlags...)
-	}
-
 	if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
 		return flags
 	}
@@ -666,9 +653,6 @@
 		flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-sanitize-coverage=stack-depth")
 		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-coverage=stack-depth")
 
-		// TODO(b/133876586): Experimental PM breaks sanitizer coverage.
-		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-experimental-new-pass-manager")
-
 		// Disable fortify for fuzzing builds. Generally, we'll be building with
 		// UBSan or ASan here and the fortify checks pollute the stack traces.
 		flags.Local.CFlags = append(flags.Local.CFlags, "-U_FORTIFY_SOURCE")
@@ -714,28 +698,27 @@
 
 	if len(sanitize.Properties.Sanitizers) > 0 {
 		sanitizeArg := "-fsanitize=" + strings.Join(sanitize.Properties.Sanitizers, ",")
-
 		flags.Local.CFlags = append(flags.Local.CFlags, sanitizeArg)
 		flags.Local.AsFlags = append(flags.Local.AsFlags, sanitizeArg)
-		if ctx.Host() {
+		flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
+
+		if ctx.toolchain().Bionic() {
+			// Bionic sanitizer runtimes have already been added as dependencies so that
+			// the right variant of the runtime will be used (with the "-android"
+			// suffix), so don't let clang the runtime library.
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-sanitize-link-runtime")
+		} else {
 			// Host sanitizers only link symbols in the final executable, so
 			// there will always be undefined symbols in intermediate libraries.
 			_, flags.Global.LdFlags = removeFromList("-Wl,--no-undefined", flags.Global.LdFlags)
-			flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
 
-			// non-Bionic toolchain prebuilts are missing UBSan's vptr and function sanitizers
-			if !ctx.toolchain().Bionic() {
-				flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
-			}
+			// non-Bionic toolchain prebuilts are missing UBSan's vptr and function san
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
 		}
 
 		if enableMinimalRuntime(sanitize) {
 			flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
-			flags.libFlags = append([]string{minimalRuntimePath}, flags.libFlags...)
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
-			if !ctx.toolchain().Bionic() {
-				flags.libFlags = append([]string{builtinsRuntimePath}, flags.libFlags...)
-			}
 		}
 
 		if Bool(sanitize.Properties.Sanitize.Fuzzer) {
@@ -1204,6 +1187,36 @@
 			}
 		}
 
+		addStaticDeps := func(deps ...string) {
+			// If we're using snapshots, redirect to snapshot whenever possible
+			snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
+			for idx, dep := range deps {
+				if lib, ok := snapshot.StaticLibs[dep]; ok {
+					deps[idx] = lib
+				}
+			}
+
+			// static executable gets static runtime libs
+			depTag := libraryDependencyTag{Kind: staticLibraryDependency}
+			variations := append(mctx.Target().Variations(),
+				blueprint.Variation{Mutator: "link", Variation: "static"})
+			if c.Device() {
+				variations = append(variations, c.ImageVariation())
+			}
+			if c.UseSdk() {
+				variations = append(variations,
+					blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
+			}
+			mctx.AddFarVariationDependencies(variations, depTag, deps...)
+
+		}
+		if enableMinimalRuntime(c.sanitize) || c.sanitize.Properties.MinimalRuntimeDep {
+			addStaticDeps(config.UndefinedBehaviorSanitizerMinimalRuntimeLibrary(toolchain))
+		}
+		if c.sanitize.Properties.BuiltinsDep {
+			addStaticDeps(config.BuiltinsRuntimeLibrary(toolchain))
+		}
+
 		if runtimeLibrary != "" && (toolchain.Bionic() || c.sanitize.Properties.UbsanRuntimeDep) {
 			// UBSan is supported on non-bionic linux host builds as well
 
@@ -1215,23 +1228,8 @@
 			// Note that by adding dependency with {static|shared}DepTag, the lib is
 			// added to libFlags and LOCAL_SHARED_LIBRARIES by cc.Module
 			if c.staticBinary() {
-				deps := append(extraStaticDeps, runtimeLibrary)
-				// If we're using snapshots, redirect to snapshot whenever possible
-				snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
-				for idx, dep := range deps {
-					if lib, ok := snapshot.StaticLibs[dep]; ok {
-						deps[idx] = lib
-					}
-				}
-
-				// static executable gets static runtime libs
-				depTag := libraryDependencyTag{Kind: staticLibraryDependency}
-				variations := append(mctx.Target().Variations(),
-					blueprint.Variation{Mutator: "link", Variation: "static"})
-				if c.Device() {
-					variations = append(variations, c.ImageVariation())
-				}
-				mctx.AddFarVariationDependencies(variations, depTag, deps...)
+				addStaticDeps(runtimeLibrary)
+				addStaticDeps(extraStaticDeps...)
 			} else if !c.static() && !c.Header() {
 				// If we're using snapshots, redirect to snapshot whenever possible
 				snapshot := mctx.Provider(SnapshotInfoProvider).(SnapshotInfo)
@@ -1256,6 +1254,10 @@
 				if c.Device() {
 					variations = append(variations, c.ImageVariation())
 				}
+				if c.UseSdk() {
+					variations = append(variations,
+						blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
+				}
 				AddSharedLibDependenciesWithVersions(mctx, c, variations, depTag, runtimeLibrary, "", true)
 			}
 			// static lib does not have dependency to the runtime library. The
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index 253a11b..753d74c 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -506,7 +506,7 @@
 }
 
 func (p *snapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
-	if p.MatchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
+	if p.MatchesWithDevice(ctx.DeviceConfig()) && p.shared() {
 		p.baseInstaller.install(ctx, file)
 	}
 }
diff --git a/cc/test.go b/cc/test.go
index f37fdae..d8b7833 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -105,11 +105,6 @@
 	// Add RunCommandTargetPreparer to stop framework before the test and start it after the test.
 	Disable_framework *bool
 
-	// Add ShippingApiLevelModuleController to auto generated test config. If the device properties
-	// for the shipping api level is less than the test_min_api_level, skip this module.
-	// Deprecated (b/187258404). Use test_options.min_shipping_api_level instead.
-	Test_min_api_level *int64
-
 	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
 	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
 	// explicitly.
@@ -432,14 +427,6 @@
 		var options []tradefed.Option
 		options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_options.Min_shipping_api_level), 10)})
 		configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options})
-	} else if test.Properties.Test_min_api_level != nil {
-		// TODO: (b/187258404) Remove test.Properties.Test_min_api_level
-		if test.Properties.Test_options.Vsr_min_shipping_api_level != nil {
-			ctx.PropertyErrorf("test_min_api_level", "must not be set at the same time as 'vsr_min_shipping_api_level'.")
-		}
-		var options []tradefed.Option
-		options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_min_api_level), 10)})
-		configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options})
 	}
 	if test.Properties.Test_options.Vsr_min_shipping_api_level != nil {
 		var options []tradefed.Option
@@ -474,7 +461,7 @@
 }
 
 func NewTest(hod android.HostOrDeviceSupported) *Module {
-	module, binary := NewBinary(hod)
+	module, binary := newBinary(hod, false)
 	module.multilib = android.MultilibBoth
 	binary.baseInstaller = NewTestInstaller()
 
@@ -564,6 +551,10 @@
 	testConfig android.Path
 }
 
+func (benchmark *benchmarkDecorator) benchmarkBinary() bool {
+	return true
+}
+
 func (benchmark *benchmarkDecorator) linkerInit(ctx BaseModuleContext) {
 	runpath := "../../lib"
 	if ctx.toolchain().Is64Bit() {
@@ -601,7 +592,7 @@
 }
 
 func NewBenchmark(hod android.HostOrDeviceSupported) *Module {
-	module, binary := NewBinary(hod)
+	module, binary := newBinary(hod, false)
 	module.multilib = android.MultilibBoth
 	binary.baseInstaller = NewBaseInstaller("benchmarktest", "benchmarktest64", InstallInData)
 
diff --git a/cc/testing.go b/cc/testing.go
index 3bf936d..a03d147 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -30,7 +30,6 @@
 	RegisterLibraryBuildComponents(ctx)
 	RegisterLibraryHeadersBuildComponents(ctx)
 
-	ctx.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
 	ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
 	ctx.RegisterModuleType("cc_object", ObjectFactory)
 	ctx.RegisterModuleType("cc_genrule", GenRuleFactory)
@@ -63,41 +62,14 @@
 
 func commonDefaultModules() string {
 	return `
-		toolchain_library {
-			name: "libcompiler_rt-extras",
+		cc_defaults {
+			name: "toolchain_libs_defaults",
 			vendor_available: true,
-			vendor_ramdisk_available: true,
 			product_available: true,
 			recovery_available: true,
-			src: "",
-		}
-
-		toolchain_library {
-			name: "libclang_rt.builtins-arm-android",
-			vendor_available: true,
-			vendor_ramdisk_available: true,
-			product_available: true,
-			recovery_available: true,
-			native_bridge_supported: true,
-			src: "",
-		}
-
-		toolchain_library {
-			name: "libclang_rt.builtins-aarch64-android",
-			vendor_available: true,
-			vendor_ramdisk_available: true,
-			product_available: true,
-			recovery_available: true,
-			native_bridge_supported: true,
-			src: "",
-		}
-
-		cc_prebuilt_library_shared {
-			name: "libclang_rt.hwasan-aarch64-android",
+			no_libcrt: true,
+			sdk_version: "minimum",
 			nocrt: true,
-			vendor_available: true,
-			product_available: true,
-			recovery_available: true,
 			system_shared_libs: [],
 			stl: "none",
 			srcs: [""],
@@ -107,87 +79,115 @@
 			},
 		}
 
-		toolchain_library {
+		cc_prebuilt_library_static {
+			name: "libcompiler_rt-extras",
+			defaults: ["toolchain_libs_defaults"],
+			vendor_ramdisk_available: true,
+		}
+
+		cc_prebuilt_library_static {
+			name: "libclang_rt.builtins-arm-android",
+			defaults: ["toolchain_libs_defaults"],
+			native_bridge_supported: true,
+			vendor_ramdisk_available: true,
+		}
+
+		cc_prebuilt_library_static {
+			name: "libclang_rt.builtins-aarch64-android",
+			defaults: ["toolchain_libs_defaults"],
+			native_bridge_supported: true,
+			vendor_ramdisk_available: true,
+		}
+
+		cc_prebuilt_library_static {
+			name: "libclang_rt.builtins-x86_64",
+			defaults: ["toolchain_libs_defaults"],
+			host_supported: true,
+		}
+
+		cc_prebuilt_library_static {
+			name: "libclang_rt.builtins-i386",
+			defaults: ["toolchain_libs_defaults"],
+			host_supported: true,
+		}
+
+		cc_prebuilt_library_shared {
+			name: "libclang_rt.hwasan-aarch64-android",
+			defaults: ["toolchain_libs_defaults"],
+		}
+
+		cc_prebuilt_library_static {
 			name: "libclang_rt.builtins-i686-android",
-			vendor_available: true,
+			defaults: ["toolchain_libs_defaults"],
 			vendor_ramdisk_available: true,
-			product_available: true,
-			recovery_available: true,
 			native_bridge_supported: true,
-			src: "",
 		}
 
-		toolchain_library {
+		cc_prebuilt_library_static {
 			name: "libclang_rt.builtins-x86_64-android",
-			defaults: ["linux_bionic_supported"],
-			vendor_available: true,
-			vendor_ramdisk_available: true,
-			product_available: true,
-			recovery_available: true,
+			defaults: [
+				"linux_bionic_supported",
+				"toolchain_libs_defaults",
+			],
 			native_bridge_supported: true,
-			src: "",
+			vendor_ramdisk_available: true,
 		}
 
-		toolchain_library {
+		cc_prebuilt_library_static {
 			name: "libunwind",
-			defaults: ["linux_bionic_supported"],
-			vendor_available: true,
+			defaults: [
+				"linux_bionic_supported",
+				"toolchain_libs_defaults",
+			],
 			vendor_ramdisk_available: true,
-			product_available: true,
-			recovery_available: true,
 			native_bridge_supported: true,
-			src: "",
 		}
 
-		toolchain_library {
+		cc_prebuilt_library_static {
 			name: "libclang_rt.fuzzer-arm-android",
-			vendor_available: true,
-			product_available: true,
-			recovery_available: true,
-			src: "",
+			defaults: ["toolchain_libs_defaults"],
 		}
 
-		toolchain_library {
+		cc_prebuilt_library_static {
 			name: "libclang_rt.fuzzer-aarch64-android",
-			vendor_available: true,
-			product_available: true,
-			recovery_available: true,
-			src: "",
+			defaults: ["toolchain_libs_defaults"],
 		}
 
-		toolchain_library {
+		cc_prebuilt_library_static {
 			name: "libclang_rt.fuzzer-i686-android",
-			vendor_available: true,
-			product_available: true,
-			recovery_available: true,
-			src: "",
+			defaults: ["toolchain_libs_defaults"],
 		}
 
-		toolchain_library {
+		cc_prebuilt_library_static {
 			name: "libclang_rt.fuzzer-x86_64-android",
-			defaults: ["linux_bionic_supported"],
-			vendor_available: true,
-			product_available: true,
-			recovery_available: true,
-			src: "",
+			defaults: [
+				"linux_bionic_supported",
+				"toolchain_libs_defaults",
+			],
 		}
 
-		toolchain_library {
+		cc_prebuilt_library_static {
 			name: "libclang_rt.fuzzer-x86_64",
-			vendor_available: true,
-			product_available: true,
-			recovery_available: true,
-			src: "",
+			defaults: [
+				"linux_bionic_supported",
+				"toolchain_libs_defaults",
+			],
 		}
 
 		// Needed for sanitizer
 		cc_prebuilt_library_shared {
 			name: "libclang_rt.ubsan_standalone-aarch64-android",
-			vendor_available: true,
-			product_available: true,
-			recovery_available: true,
-			system_shared_libs: [],
-			srcs: [""],
+			defaults: ["toolchain_libs_defaults"],
+		}
+
+		cc_prebuilt_library_static {
+			name: "libclang_rt.ubsan_minimal-aarch64-android",
+			defaults: ["toolchain_libs_defaults"],
+		}
+
+		cc_prebuilt_library_static {
+			name: "libclang_rt.ubsan_minimal-arm-android",
+			defaults: ["toolchain_libs_defaults"],
 		}
 
 		cc_library {
@@ -480,7 +480,7 @@
 
 func withWindowsModules() string {
 	return `
-		toolchain_library {
+		cc_prebuilt_library_static {
 			name: "libwinpthread",
 			host_supported: true,
 			enabled: false,
@@ -489,7 +489,8 @@
 					enabled: true,
 				},
 			},
-			src: "",
+			stl: "none",
+			srcs:[""],
 		}
 		`
 }
diff --git a/cc/tidy.go b/cc/tidy.go
index 53ff156..750e9de 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -15,6 +15,7 @@
 package cc
 
 import (
+	"path/filepath"
 	"regexp"
 	"strings"
 
@@ -70,13 +71,17 @@
 		return flags
 	}
 
-	// If not explicitly set, check the global tidy flag
-	if tidy.Properties.Tidy == nil && !ctx.Config().ClangTidy() {
-		return flags
-	}
-
+	// If not explicitly disabled, set flags.Tidy to generate .tidy rules.
+	// Note that libraries and binaries will depend on .tidy files ONLY if
+	// the global WITH_TIDY or module 'tidy' property is true.
 	flags.Tidy = true
 
+	// If explicitly enabled, by global default or local tidy property,
+	// set flags.NeedTidyFiles to make this module depend on .tidy files.
+	if ctx.Config().ClangTidy() || Bool(tidy.Properties.Tidy) {
+		flags.NeedTidyFiles = true
+	}
+
 	// Add global WITH_TIDY_FLAGS and local tidy_flags.
 	withTidyFlags := ctx.Config().Getenv("WITH_TIDY_FLAGS")
 	if len(withTidyFlags) > 0 {
@@ -97,6 +102,12 @@
 		}
 		flags.TidyFlags = append(flags.TidyFlags, headerFilter)
 	}
+	// Work around RBE bug in parsing clang-tidy flags, replace "--flag" with "-flag".
+	// Some C/C++ modules added local tidy flags like --header-filter= and --extra-arg-before=.
+	doubleDash := regexp.MustCompile("^('?)--(.*)$")
+	for i, s := range flags.TidyFlags {
+		flags.TidyFlags[i] = doubleDash.ReplaceAllString(s, "$1-$2")
+	}
 
 	// If clang-tidy is not enabled globally, add the -quiet flag.
 	if !ctx.Config().ClangTidy() {
@@ -151,6 +162,9 @@
 	// 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"
+	// http://b/216364337 - TODO: Follow-up after compiler update to
+	// disable or fix individual instances.
+	tidyChecks = tidyChecks + ",-cert-err33-c"
 	flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
 
 	if ctx.Config().IsEnvTrue("WITH_TIDY") {
@@ -183,3 +197,154 @@
 	}
 	return flags
 }
+
+func init() {
+	android.RegisterSingletonType("tidy_phony_targets", TidyPhonySingleton)
+}
+
+// This TidyPhonySingleton generates both tidy-* and obj-* phony targets for C/C++ files.
+func TidyPhonySingleton() android.Singleton {
+	return &tidyPhonySingleton{}
+}
+
+type tidyPhonySingleton struct{}
+
+// Given a final module, add its tidy/obj phony targets to tidy/objModulesInDirGroup.
+func collectTidyObjModuleTargets(ctx android.SingletonContext, module android.Module,
+	tidyModulesInDirGroup, objModulesInDirGroup map[string]map[string]android.Paths) {
+	allObjFileGroups := make(map[string]android.Paths)     // variant group name => obj file Paths
+	allTidyFileGroups := make(map[string]android.Paths)    // variant group name => tidy file Paths
+	subsetObjFileGroups := make(map[string]android.Paths)  // subset group name => obj file Paths
+	subsetTidyFileGroups := make(map[string]android.Paths) // subset group name => tidy file Paths
+
+	// (1) Collect all obj/tidy files into OS-specific groups.
+	ctx.VisitAllModuleVariants(module, func(variant android.Module) {
+		if ctx.Config().KatiEnabled() && android.ShouldSkipAndroidMkProcessing(variant) {
+			return
+		}
+		if m, ok := variant.(*Module); ok {
+			osName := variant.Target().Os.Name
+			addToOSGroup(osName, m.objFiles, allObjFileGroups, subsetObjFileGroups)
+			addToOSGroup(osName, m.tidyFiles, allTidyFileGroups, subsetTidyFileGroups)
+		}
+	})
+
+	// (2) Add an all-OS group, with "" or "subset" name, to include all os-specific phony targets.
+	addAllOSGroup(ctx, module, allObjFileGroups, "", "obj")
+	addAllOSGroup(ctx, module, allTidyFileGroups, "", "tidy")
+	addAllOSGroup(ctx, module, subsetObjFileGroups, "subset", "obj")
+	addAllOSGroup(ctx, module, subsetTidyFileGroups, "subset", "tidy")
+
+	tidyTargetGroups := make(map[string]android.Path)
+	objTargetGroups := make(map[string]android.Path)
+	genObjTidyPhonyTargets(ctx, module, "obj", allObjFileGroups, objTargetGroups)
+	genObjTidyPhonyTargets(ctx, module, "obj", subsetObjFileGroups, objTargetGroups)
+	genObjTidyPhonyTargets(ctx, module, "tidy", allTidyFileGroups, tidyTargetGroups)
+	genObjTidyPhonyTargets(ctx, module, "tidy", subsetTidyFileGroups, tidyTargetGroups)
+
+	moduleDir := ctx.ModuleDir(module)
+	appendToModulesInDirGroup(tidyTargetGroups, moduleDir, tidyModulesInDirGroup)
+	appendToModulesInDirGroup(objTargetGroups, moduleDir, objModulesInDirGroup)
+}
+
+func (m *tidyPhonySingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	// For tidy-* directory phony targets, there are different variant groups.
+	// tidyModulesInDirGroup[G][D] is for group G, directory D, with Paths
+	// of all phony targets to be included into direct dependents of tidy-D_G.
+	tidyModulesInDirGroup := make(map[string]map[string]android.Paths)
+	// Also for obj-* directory phony targets.
+	objModulesInDirGroup := make(map[string]map[string]android.Paths)
+
+	// Collect tidy/obj targets from the 'final' modules.
+	ctx.VisitAllModules(func(module android.Module) {
+		if module == ctx.FinalModule(module) {
+			collectTidyObjModuleTargets(ctx, module, tidyModulesInDirGroup, objModulesInDirGroup)
+		}
+	})
+
+	suffix := ""
+	if ctx.Config().KatiEnabled() {
+		suffix = "-soong"
+	}
+	generateObjTidyPhonyTargets(ctx, suffix, "obj", objModulesInDirGroup)
+	generateObjTidyPhonyTargets(ctx, suffix, "tidy", tidyModulesInDirGroup)
+}
+
+// The name for an obj/tidy module variant group phony target is Name_group-obj/tidy,
+func objTidyModuleGroupName(module android.Module, group string, suffix string) string {
+	if group == "" {
+		return module.Name() + "-" + suffix
+	}
+	return module.Name() + "_" + group + "-" + suffix
+}
+
+// Generate obj-* or tidy-* phony targets.
+func generateObjTidyPhonyTargets(ctx android.SingletonContext, suffix string, prefix string, objTidyModulesInDirGroup map[string]map[string]android.Paths) {
+	// For each variant group, create a <prefix>-<directory>_group target that
+	// depends on all subdirectories and modules in the directory.
+	for group, modulesInDir := range objTidyModulesInDirGroup {
+		groupSuffix := ""
+		if group != "" {
+			groupSuffix = "_" + group
+		}
+		mmTarget := func(dir string) string {
+			return prefix + "-" + strings.Replace(filepath.Clean(dir), "/", "-", -1) + groupSuffix
+		}
+		dirs, topDirs := android.AddAncestors(ctx, modulesInDir, mmTarget)
+		// Create a <prefix>-soong_group target that depends on all <prefix>-dir_group of top level dirs.
+		var topDirPaths android.Paths
+		for _, dir := range topDirs {
+			topDirPaths = append(topDirPaths, android.PathForPhony(ctx, mmTarget(dir)))
+		}
+		ctx.Phony(prefix+suffix+groupSuffix, topDirPaths...)
+		// Create a <prefix>-dir_group target that depends on all targets in modulesInDir[dir]
+		for _, dir := range dirs {
+			if dir != "." && dir != "" {
+				ctx.Phony(mmTarget(dir), modulesInDir[dir]...)
+			}
+		}
+	}
+}
+
+// Append (obj|tidy)TargetGroups[group] into (obj|tidy)ModulesInDirGroups[group][moduleDir].
+func appendToModulesInDirGroup(targetGroups map[string]android.Path, moduleDir string, modulesInDirGroup map[string]map[string]android.Paths) {
+	for group, phonyPath := range targetGroups {
+		if _, found := modulesInDirGroup[group]; !found {
+			modulesInDirGroup[group] = make(map[string]android.Paths)
+		}
+		modulesInDirGroup[group][moduleDir] = append(modulesInDirGroup[group][moduleDir], phonyPath)
+	}
+}
+
+// Add given files to the OS group and subset group.
+func addToOSGroup(osName string, files android.Paths, allGroups, subsetGroups map[string]android.Paths) {
+	if len(files) > 0 {
+		subsetName := osName + "_subset"
+		allGroups[osName] = append(allGroups[osName], files...)
+		// Now include only the first variant in the subsetGroups.
+		// If clang and clang-tidy get faster, we might include more variants.
+		if _, found := subsetGroups[subsetName]; !found {
+			subsetGroups[subsetName] = files
+		}
+	}
+}
+
+// Add an all-OS group, with groupName, to include all os-specific phony targets.
+func addAllOSGroup(ctx android.SingletonContext, module android.Module, phonyTargetGroups map[string]android.Paths, groupName string, objTidyName string) {
+	if len(phonyTargetGroups) > 0 {
+		var targets android.Paths
+		for group, _ := range phonyTargetGroups {
+			targets = append(targets, android.PathForPhony(ctx, objTidyModuleGroupName(module, group, objTidyName)))
+		}
+		phonyTargetGroups[groupName] = targets
+	}
+}
+
+// Create one phony targets for each group and add them to the targetGroups.
+func genObjTidyPhonyTargets(ctx android.SingletonContext, module android.Module, objTidyName string, fileGroups map[string]android.Paths, targetGroups map[string]android.Path) {
+	for group, files := range fileGroups {
+		groupName := objTidyModuleGroupName(module, group, objTidyName)
+		ctx.Phony(groupName, files...)
+		targetGroups[group] = android.PathForPhony(ctx, groupName)
+	}
+}
diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go
deleted file mode 100644
index bda73ea..0000000
--- a/cc/toolchain_library.go
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2016 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 cc
-
-import (
-	"android/soong/android"
-)
-
-//
-// Device libraries shipped with gcc
-//
-
-func init() {
-	android.RegisterModuleType("toolchain_library", ToolchainLibraryFactory)
-}
-
-type toolchainLibraryProperties struct {
-	// the prebuilt toolchain library, as a path from the top of the source tree
-	Src *string `android:"arch_variant"`
-
-	// Repack the archive with only the selected objects.
-	Repack_objects_to_keep []string `android:"arch_variant"`
-}
-
-type toolchainLibraryDecorator struct {
-	*libraryDecorator
-	stripper Stripper
-
-	Properties toolchainLibraryProperties
-}
-
-func (*toolchainLibraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	// toolchain libraries can't have any dependencies
-	return deps
-}
-
-func (library *toolchainLibraryDecorator) linkerProps() []interface{} {
-	var props []interface{}
-	props = append(props, library.libraryDecorator.linkerProps()...)
-	return append(props, &library.Properties, &library.stripper.StripProperties)
-}
-
-// toolchain_library is used internally by the build tool to link the specified
-// static library in src property to the device libraries that are shipped with
-// gcc.
-func ToolchainLibraryFactory() android.Module {
-	module, library := NewLibrary(android.HostAndDeviceSupported)
-	library.BuildOnlyStatic()
-	toolchainLibrary := &toolchainLibraryDecorator{
-		libraryDecorator: library,
-	}
-	module.compiler = toolchainLibrary
-	module.linker = toolchainLibrary
-	module.stl = nil
-	module.sanitize = nil
-	module.installer = nil
-	module.library = toolchainLibrary
-	module.Properties.Sdk_version = StringPtr("current")
-	return module.Init()
-}
-
-func (library *toolchainLibraryDecorator) compile(ctx ModuleContext, flags Flags,
-	deps PathDeps) Objects {
-	return Objects{}
-}
-
-func (library *toolchainLibraryDecorator) link(ctx ModuleContext,
-	flags Flags, deps PathDeps, objs Objects) android.Path {
-
-	if library.Properties.Src == nil {
-		ctx.PropertyErrorf("src", "No library source specified")
-		return android.PathForSource(ctx, "")
-	}
-
-	srcPath := android.PathForSource(ctx, *library.Properties.Src)
-	outputFile := android.Path(srcPath)
-
-	if library.Properties.Repack_objects_to_keep != nil {
-		fileName := ctx.ModuleName() + staticLibraryExtension
-		repackedPath := android.PathForModuleOut(ctx, fileName)
-		transformArchiveRepack(ctx, outputFile, repackedPath, library.Properties.Repack_objects_to_keep)
-		outputFile = repackedPath
-	}
-
-	if library.stripper.StripProperties.Strip.Keep_symbols_list != nil {
-		fileName := ctx.ModuleName() + staticLibraryExtension
-		strippedPath := android.PathForModuleOut(ctx, fileName)
-		stripFlags := flagsToStripFlags(flags)
-		library.stripper.StripStaticLib(ctx, outputFile, strippedPath, stripFlags)
-		outputFile = strippedPath
-	}
-
-	depSet := android.NewDepSetBuilder(android.TOPOLOGICAL).Direct(outputFile).Build()
-	ctx.SetProvider(StaticLibraryInfoProvider, StaticLibraryInfo{
-		StaticLibrary: outputFile,
-
-		TransitiveStaticLibrariesForOrdering: depSet,
-	})
-
-	return outputFile
-}
-
-func (library *toolchainLibraryDecorator) nativeCoverage() bool {
-	return false
-}
diff --git a/cc/util.go b/cc/util.go
index 88b0aba..b256b9a 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -85,6 +85,7 @@
 		toolchain:     in.Toolchain,
 		gcovCoverage:  in.GcovCoverage,
 		tidy:          in.Tidy,
+		needTidyFiles: in.NeedTidyFiles,
 		sAbiDump:      in.SAbiDump,
 		emitXrefs:     in.EmitXrefs,
 
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index b5022c8..645b2cc 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -65,10 +65,13 @@
 		nocrt: true,
 	}
 
-	toolchain_library {
+	cc_prebuilt_library_static {
 		name: "libb",
 		vendor_available: true,
-		src: "libb.a",
+		srcs: ["libb.a"],
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
 	}
 
 	cc_object {
@@ -1222,10 +1225,13 @@
 		nocrt: true,
 	}
 
-	toolchain_library {
+	cc_prebuilt_library_static {
 		name: "libb",
 		recovery_available: true,
-		src: "libb.a",
+		srcs: ["libb.a"],
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
 	}
 
 	cc_object {
diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go
index 1280553..5603b41 100644
--- a/cmd/extract_linker/main.go
+++ b/cmd/extract_linker/main.go
@@ -114,6 +114,10 @@
 
 	fmt.Fprintln(asm, `.section .note.android.embedded_linker,"a",%note`)
 
+	// Discard the PT_INTERP section so that the linker doesn't need to be passed the
+	// --no-dynamic-linker flag.
+	fmt.Println(script, "    /DISCARD/ : { *(.interp) }")
+
 	fmt.Fprintln(script, "}")
 	fmt.Fprintln(script, "INSERT BEFORE .note.android.embedded_linker;")
 
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 0577c86..7cb8ab7 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -407,10 +407,10 @@
 
 	s.Finish()
 
-	if failures == 1 {
+	if failures.count == 1 {
 		log.Fatal("1 failure")
-	} else if failures > 1 {
-		log.Fatalf("%d failures", failures)
+	} else if failures.count > 1 {
+		log.Fatalf("%d failures %q", failures.count, failures.fails)
 	} else {
 		fmt.Fprintln(output, "Success")
 	}
@@ -522,19 +522,23 @@
 	})
 }
 
-type failureCount int
+type failureCount struct {
+	count int
+	fails []string
+}
 
 func (f *failureCount) StartAction(action *status.Action, counts status.Counts) {}
 
 func (f *failureCount) FinishAction(result status.ActionResult, counts status.Counts) {
 	if result.Error != nil {
-		*f += 1
+		f.count += 1
+		f.fails = append(f.fails, result.Action.Description)
 	}
 }
 
 func (f *failureCount) Message(level status.MsgLevel, message string) {
 	if level >= status.ErrorLvl {
-		*f += 1
+		f.count += 1
 	}
 }
 
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index dfc4eae..b3a6ee0 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -48,6 +48,7 @@
 	delvePath   string
 
 	moduleGraphFile   string
+	moduleActionsFile string
 	docFile           string
 	bazelQueryViewDir string
 	bp2buildMarker    string
@@ -76,6 +77,7 @@
 
 	// Flags representing various modes soong_build can run in
 	flag.StringVar(&moduleGraphFile, "module_graph_file", "", "JSON module graph file to output")
+	flag.StringVar(&moduleActionsFile, "module_actions_file", "", "JSON file to output inputs/outputs of actions of modules")
 	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")
@@ -176,15 +178,17 @@
 	}
 }
 
-func writeJsonModuleGraph(ctx *android.Context, path string) {
-	f, err := os.Create(shared.JoinPath(topDir, path))
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "%s", err)
+func writeJsonModuleGraphAndActions(ctx *android.Context, graphPath string, actionsPath string) {
+	graphFile, graphErr := os.Create(shared.JoinPath(topDir, graphPath))
+	actionsFile, actionsErr := os.Create(shared.JoinPath(topDir, actionsPath))
+	if graphErr != nil || actionsErr != nil {
+		fmt.Fprintf(os.Stderr, "Graph err: %s, actions err: %s", graphErr, actionsErr)
 		os.Exit(1)
 	}
 
-	defer f.Close()
-	ctx.Context.PrintJSONGraph(f)
+	defer graphFile.Close()
+	defer actionsFile.Close()
+	ctx.Context.PrintJSONGraphAndActions(graphFile, actionsFile)
 }
 
 func writeBuildGlobsNinjaFile(srcDir, buildDir string, globs func() pathtools.MultipleGlobResults, config interface{}) []string {
@@ -254,7 +258,7 @@
 			writeDepFile(queryviewMarkerFile, ninjaDeps)
 			return queryviewMarkerFile
 		} else if generateModuleGraphFile {
-			writeJsonModuleGraph(ctx, moduleGraphFile)
+			writeJsonModuleGraphAndActions(ctx, moduleGraphFile, moduleActionsFile)
 			writeDepFile(moduleGraphFile, ninjaDeps)
 			return moduleGraphFile
 		} else if generateDocFile {
@@ -386,7 +390,7 @@
 // - won't be overwritten by corresponding bp2build generated files
 //
 // And return their paths so they can be left out of the Bazel workspace dir (i.e. ignored)
-func getPathsToIgnoredBuildFiles(topDir string, generatedRoot string, srcDirBazelFiles []string) []string {
+func getPathsToIgnoredBuildFiles(topDir string, generatedRoot string, srcDirBazelFiles []string, verbose bool) []string {
 	paths := make([]string, 0)
 
 	for _, srcDirBazelFileRelativePath := range srcDirBazelFiles {
@@ -416,7 +420,9 @@
 			// BUILD file clash resolution happens later in the symlink forest creation
 			continue
 		}
-		fmt.Fprintf(os.Stderr, "Ignoring existing BUILD file: %s\n", srcDirBazelFileRelativePath)
+		if verbose {
+			fmt.Fprintf(os.Stderr, "Ignoring existing BUILD file: %s\n", srcDirBazelFileRelativePath)
+		}
 		paths = append(paths, srcDirBazelFileRelativePath)
 	}
 
@@ -523,7 +529,7 @@
 		os.Exit(1)
 	}
 
-	pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles)
+	pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
 	excludes = append(excludes, pathsToIgnoredBuildFiles...)
 
 	excludes = append(excludes, getTemporaryExcludes()...)
@@ -535,6 +541,7 @@
 	// for queryview, since that's a total repo-wide conversion and there's a
 	// 1:1 mapping for each module.
 	metrics.Print()
+	writeBp2BuildMetrics(&metrics, configuration)
 
 	ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
 	ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
@@ -544,3 +551,13 @@
 	// Create an empty bp2build marker file.
 	touch(shared.JoinPath(topDir, bp2buildMarker))
 }
+
+// Write Bp2Build metrics into $LOG_DIR
+func writeBp2BuildMetrics(metrics *bp2build.CodegenMetrics, configuration android.Config) {
+	metricsDir := configuration.Getenv("LOG_DIR")
+	if len(metricsDir) < 1 {
+		fmt.Fprintf(os.Stderr, "\nMissing required env var for generating bp2build metrics: LOG_DIR\n")
+		os.Exit(1)
+	}
+	metrics.Write(metricsDir)
+}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 9ee373e..a0cfbea 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -16,7 +16,6 @@
 
 import (
 	"context"
-	"encoding/json"
 	"flag"
 	"fmt"
 	"io/ioutil"
@@ -24,6 +23,7 @@
 	"path/filepath"
 	"strconv"
 	"strings"
+	"syscall"
 	"time"
 
 	"android/soong/shared"
@@ -36,11 +36,6 @@
 	"android/soong/ui/tracer"
 )
 
-const (
-	configDir  = "vendor/google/tools/soong_config"
-	jsonSuffix = "json"
-)
-
 // A command represents an operation to be executed in the soong build
 // system.
 type command struct {
@@ -67,7 +62,7 @@
 }
 
 // list of supported commands (flags) supported by soong ui
-var commands []command = []command{
+var commands = []command{
 	{
 		flag:        "--make-mode",
 		description: "build the modules by the target name (i.e. soong_docs)",
@@ -117,34 +112,6 @@
 	return indexList(s, list) != -1
 }
 
-func loadEnvConfig() error {
-	bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
-	if bc == "" {
-		return nil
-	}
-	cfgFile := filepath.Join(os.Getenv("TOP"), configDir, fmt.Sprintf("%s.%s", bc, jsonSuffix))
-
-	envVarsJSON, err := ioutil.ReadFile(cfgFile)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "\033[33mWARNING:\033[0m failed to open config file %s: %s\n", cfgFile, err.Error())
-		return nil
-	}
-
-	var envVars map[string]map[string]string
-	if err := json.Unmarshal(envVarsJSON, &envVars); err != nil {
-		return fmt.Errorf("env vars config file: %s did not parse correctly: %s", cfgFile, err.Error())
-	}
-	for k, v := range envVars["env"] {
-		if os.Getenv(k) != "" {
-			continue
-		}
-		if err := os.Setenv(k, v); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
 // Main execution of soong_ui. The command format is as follows:
 //
 //    soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
@@ -207,11 +174,6 @@
 		Status:  stat,
 	}}
 
-	if err := loadEnvConfig(); err != nil {
-		fmt.Fprintf(os.Stderr, "failed to parse env config files: %v", err)
-		os.Exit(1)
-	}
-
 	config := c.config(buildCtx, args...)
 
 	build.SetupOutDir(buildCtx, config)
@@ -243,6 +205,8 @@
 	buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
 		config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
 
+	setMaxFiles(buildCtx)
+
 	{
 		// The order of the function calls is important. The last defer function call
 		// is the first one that is executed to save the rbe metrics to a protobuf
@@ -562,7 +526,11 @@
 	}
 
 	// command not found
-	return nil, nil, fmt.Errorf("Command not found: %q", args)
+	flags := make([]string, len(commands))
+	for i, c := range commands {
+		flags[i] = c.flag
+	}
+	return nil, nil, fmt.Errorf("Command not found: %q\nDid you mean one of these: %q", args, flags)
 }
 
 // For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
@@ -639,3 +607,24 @@
 		}
 	}
 }
+
+func setMaxFiles(ctx build.Context) {
+	var limits syscall.Rlimit
+
+	err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
+	if err != nil {
+		ctx.Println("Failed to get file limit:", err)
+		return
+	}
+
+	ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
+	if limits.Cur == limits.Max {
+		return
+	}
+
+	limits.Cur = limits.Max
+	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
+	if err != nil {
+		ctx.Println("Failed to increase file limit:", err)
+	}
+}
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 658e8e2..36513b6 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -210,6 +210,34 @@
 	Subcontexts []*ClassLoaderContext
 }
 
+// excludeLibs excludes the libraries from this ClassLoaderContext.
+//
+// This treats the supplied context as being immutable (as it may come from a dependency). So, it
+// implements copy-on-exclusion logic. That means that if any of the excluded libraries are used
+// within this context then this will return a deep copy of this without those libraries.
+//
+// If this ClassLoaderContext matches one of the libraries to exclude then this returns (nil, true)
+// to indicate that this context should be excluded from the containing list.
+//
+// If any of this ClassLoaderContext's Subcontexts reference the excluded libraries then this
+// returns a pointer to a copy of this without the excluded libraries and true to indicate that this
+// was copied.
+//
+// Otherwise, this returns a pointer to this and false to indicate that this was not copied.
+func (c *ClassLoaderContext) excludeLibs(excludedLibs []string) (*ClassLoaderContext, bool) {
+	if android.InList(c.Name, excludedLibs) {
+		return nil, true
+	}
+
+	if excludedList, modified := excludeLibsFromCLCList(c.Subcontexts, excludedLibs); modified {
+		clcCopy := *c
+		clcCopy.Subcontexts = excludedList
+		return &clcCopy, true
+	}
+
+	return c, false
+}
+
 // ClassLoaderContextMap is a map from SDK version to CLC. There is a special entry with key
 // AnySdkVersion that stores unconditional CLC that is added regardless of the target SDK version.
 //
@@ -408,6 +436,67 @@
 	return string(bytes)
 }
 
+// excludeLibsFromCLCList excludes the libraries from the ClassLoaderContext in this list.
+//
+// This treats the supplied list as being immutable (as it may come from a dependency). So, it
+// implements copy-on-exclusion logic. That means that if any of the excluded libraries are used
+// within the contexts in the list then this will return a deep copy of the list without those
+// libraries.
+//
+// If any of the ClassLoaderContext in the list reference the excluded libraries then this returns a
+// copy of this list without the excluded libraries and true to indicate that this was copied.
+//
+// Otherwise, this returns the list and false to indicate that this was not copied.
+func excludeLibsFromCLCList(clcList []*ClassLoaderContext, excludedLibs []string) ([]*ClassLoaderContext, bool) {
+	modifiedList := false
+	copiedList := make([]*ClassLoaderContext, 0, len(clcList))
+	for _, clc := range clcList {
+		resultClc, modifiedClc := clc.excludeLibs(excludedLibs)
+		if resultClc != nil {
+			copiedList = append(copiedList, resultClc)
+		}
+		modifiedList = modifiedList || modifiedClc
+	}
+
+	if modifiedList {
+		return copiedList, true
+	} else {
+		return clcList, false
+	}
+}
+
+// ExcludeLibs excludes the libraries from the ClassLoaderContextMap.
+//
+// If the list o libraries is empty then this returns the ClassLoaderContextMap.
+//
+// This treats the ClassLoaderContextMap as being immutable (as it may come from a dependency). So,
+// it implements copy-on-exclusion logic. That means that if any of the excluded libraries are used
+// within the contexts in the map then this will return a deep copy of the map without those
+// libraries.
+//
+// Otherwise, this returns the map unchanged.
+func (clcMap ClassLoaderContextMap) ExcludeLibs(excludedLibs []string) ClassLoaderContextMap {
+	if len(excludedLibs) == 0 {
+		return clcMap
+	}
+
+	excludedClcMap := make(ClassLoaderContextMap)
+	modifiedMap := false
+	for sdkVersion, clcList := range clcMap {
+		excludedList, modifiedList := excludeLibsFromCLCList(clcList, excludedLibs)
+		if len(excludedList) != 0 {
+			excludedClcMap[sdkVersion] = excludedList
+		}
+		modifiedMap = modifiedMap || modifiedList
+	}
+
+	if modifiedMap {
+		return excludedClcMap
+	} else {
+		return clcMap
+	}
+}
+
 // Now that the full unconditional context is known, reconstruct conditional context.
 // Apply filters for individual libraries, mirroring what the PackageManager does when it
 // constructs class loader context on device.
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index 4a3d390..5d3a9d9 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -284,6 +284,111 @@
 	})
 }
 
+func TestCLCMExcludeLibs(t *testing.T) {
+	ctx := testContext()
+	const optional = false
+	const implicit = true
+
+	excludeLibs := func(t *testing.T, m ClassLoaderContextMap, excluded_libs ...string) ClassLoaderContextMap {
+		// Dump the CLCM before creating a new copy that excludes a specific set of libraries.
+		before := m.Dump()
+
+		// Create a new CLCM that excludes some libraries.
+		c := m.ExcludeLibs(excluded_libs)
+
+		// Make sure that the original CLCM was not changed.
+		after := m.Dump()
+		android.AssertStringEquals(t, "input CLCM modified", before, after)
+
+		return c
+	}
+
+	t.Run("exclude nothing", func(t *testing.T) {
+		m := make(ClassLoaderContextMap)
+		m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+
+		a := excludeLibs(t, m)
+
+		android.AssertStringEquals(t, "output CLCM ", `{
+  "28": [
+    {
+      "Name": "a",
+      "Optional": false,
+      "Implicit": true,
+      "Host": "out/soong/a.jar",
+      "Device": "/system/a.jar",
+      "Subcontexts": []
+    }
+  ]
+}`, a.Dump())
+	})
+
+	t.Run("one item from list", func(t *testing.T) {
+		m := make(ClassLoaderContextMap)
+		m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+		m.AddContext(ctx, 28, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+
+		a := excludeLibs(t, m, "a")
+
+		expected := `{
+  "28": [
+    {
+      "Name": "b",
+      "Optional": false,
+      "Implicit": true,
+      "Host": "out/soong/b.jar",
+      "Device": "/system/b.jar",
+      "Subcontexts": []
+    }
+  ]
+}`
+		android.AssertStringEquals(t, "output CLCM ", expected, a.Dump())
+	})
+
+	t.Run("all items from a list", func(t *testing.T) {
+		m := make(ClassLoaderContextMap)
+		m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+		m.AddContext(ctx, 28, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+
+		a := excludeLibs(t, m, "a", "b")
+
+		android.AssertStringEquals(t, "output CLCM ", `{}`, a.Dump())
+	})
+
+	t.Run("items from a subcontext", func(t *testing.T) {
+		s := make(ClassLoaderContextMap)
+		s.AddContext(ctx, AnySdkVersion, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+		s.AddContext(ctx, AnySdkVersion, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+
+		m := make(ClassLoaderContextMap)
+		m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), s)
+
+		a := excludeLibs(t, m, "b")
+
+		android.AssertStringEquals(t, "output CLCM ", `{
+  "28": [
+    {
+      "Name": "a",
+      "Optional": false,
+      "Implicit": true,
+      "Host": "out/soong/a.jar",
+      "Device": "/system/a.jar",
+      "Subcontexts": [
+        {
+          "Name": "c",
+          "Optional": false,
+          "Implicit": true,
+          "Host": "out/soong/c.jar",
+          "Device": "/system/c.jar",
+          "Subcontexts": []
+        }
+      ]
+    }
+  ]
+}`, a.Dump())
+	})
+}
+
 func checkError(t *testing.T, have error, want string) {
 	if have == nil {
 		t.Errorf("\nwant error: '%s'\nhave: none", want)
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 6d6b41d..153b025 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -100,6 +100,48 @@
 	RelaxUsesLibraryCheck bool
 }
 
+var allPlatformSystemServerJarsKey = android.NewOnceKey("allPlatformSystemServerJars")
+
+// Returns all jars on the platform that system_server loads, including those on classpath and those
+// loaded dynamically.
+func (g *GlobalConfig) AllPlatformSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList {
+	return ctx.Config().Once(allPlatformSystemServerJarsKey, func() interface{} {
+		res := g.SystemServerJars.AppendList(&g.StandaloneSystemServerJars)
+		return &res
+	}).(*android.ConfiguredJarList)
+}
+
+var allApexSystemServerJarsKey = android.NewOnceKey("allApexSystemServerJars")
+
+// Returns all jars delivered via apex that system_server loads, including those on classpath and
+// those loaded dynamically.
+func (g *GlobalConfig) AllApexSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList {
+	return ctx.Config().Once(allApexSystemServerJarsKey, func() interface{} {
+		res := g.ApexSystemServerJars.AppendList(&g.ApexStandaloneSystemServerJars)
+		return &res
+	}).(*android.ConfiguredJarList)
+}
+
+var allSystemServerClasspathJarsKey = android.NewOnceKey("allSystemServerClasspathJars")
+
+// Returns all system_server classpath jars.
+func (g *GlobalConfig) AllSystemServerClasspathJars(ctx android.PathContext) *android.ConfiguredJarList {
+	return ctx.Config().Once(allSystemServerClasspathJarsKey, func() interface{} {
+		res := g.SystemServerJars.AppendList(&g.ApexSystemServerJars)
+		return &res
+	}).(*android.ConfiguredJarList)
+}
+
+var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars")
+
+// Returns all jars that system_server loads.
+func (g *GlobalConfig) AllSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList {
+	return ctx.Config().Once(allSystemServerJarsKey, func() interface{} {
+		res := g.AllPlatformSystemServerJars(ctx).AppendList(g.AllApexSystemServerJars(ctx))
+		return &res
+	}).(*android.ConfiguredJarList)
+}
+
 // GlobalSoongConfig contains the global config that is generated from Soong,
 // stored in dexpreopt_soong.config.
 type GlobalSoongConfig struct {
@@ -366,6 +408,7 @@
 
 type dex2oatDependencyTag struct {
 	blueprint.BaseDependencyTag
+	android.LicenseAnnotationToolchainDependencyTag
 }
 
 func (d dex2oatDependencyTag) ExcludeFromVisibilityEnforcement() {
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 3145315..de139c4 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -115,7 +115,7 @@
 	// /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) &&
-		!AllSystemServerJars(ctx, global).ContainsJar(module.Name) && !module.PreoptExtractedApk {
+		!global.AllSystemServerJars(ctx).ContainsJar(module.Name) && !module.PreoptExtractedApk {
 		return true
 	}
 
@@ -197,8 +197,8 @@
 }
 
 // Returns the dex location of a system server java library.
-func GetSystemServerDexLocation(global *GlobalConfig, lib string) string {
-	if apex := global.ApexSystemServerJars.ApexOfJar(lib); apex != "" {
+func GetSystemServerDexLocation(ctx android.PathContext, global *GlobalConfig, lib string) string {
+	if apex := global.AllApexSystemServerJars(ctx).ApexOfJar(lib); apex != "" {
 		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apex, lib)
 	}
 	return fmt.Sprintf("/system/framework/%s.jar", lib)
@@ -240,7 +240,8 @@
 
 	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
 
-	systemServerJars := AllSystemServerJars(ctx, global)
+	systemServerJars := global.AllSystemServerJars(ctx)
+	systemServerClasspathJars := global.AllSystemServerClasspathJars(ctx)
 
 	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
 	rule.Command().FlagWithOutput("rm -f ", odexPath)
@@ -251,10 +252,15 @@
 
 		var clcHost android.Paths
 		var clcTarget []string
-		for i := 0; i < jarIndex; i++ {
-			lib := systemServerJars.Jar(i)
+		endIndex := systemServerClasspathJars.IndexOfJar(module.Name)
+		if endIndex < 0 {
+			// The jar is a standalone one. Use the full classpath as the class loader context.
+			endIndex = systemServerClasspathJars.Len()
+		}
+		for i := 0; i < endIndex; i++ {
+			lib := systemServerClasspathJars.Jar(i)
 			clcHost = append(clcHost, SystemServerDexJarHostPath(ctx, lib))
-			clcTarget = append(clcTarget, GetSystemServerDexLocation(global, lib))
+			clcTarget = append(clcTarget, GetSystemServerDexLocation(ctx, global, lib))
 		}
 
 		if DexpreoptRunningInSoong {
@@ -270,12 +276,22 @@
 			// cannot see the rule in the generated dexpreopt.sh script).
 		}
 
-		checkSystemServerOrder(ctx, jarIndex)
+		clcHostString := "PCL[" + strings.Join(clcHost.Strings(), ":") + "]"
+		clcTargetString := "PCL[" + strings.Join(clcTarget, ":") + "]"
+
+		if systemServerClasspathJars.ContainsJar(module.Name) {
+			checkSystemServerOrder(ctx, jarIndex)
+		} else {
+			// Standalone jars are loaded by separate class loaders with SYSTEMSERVERCLASSPATH as the
+			// parent.
+			clcHostString = "PCL[];" + clcHostString
+			clcTargetString = "PCL[];" + clcTargetString
+		}
 
 		rule.Command().
-			Text("class_loader_context_arg=--class-loader-context=PCL[" + strings.Join(clcHost.Strings(), ":") + "]").
+			Text(`class_loader_context_arg=--class-loader-context="` + clcHostString + `"`).
 			Implicits(clcHost).
-			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clcTarget, ":") + "]")
+			Text(`stored_class_loader_context_arg=--stored-class-loader-context="` + clcTargetString + `"`)
 
 	} else {
 		// There are three categories of Java modules handled here:
@@ -533,17 +549,6 @@
 	}
 }
 
-var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars")
-
-// TODO: eliminate the superficial global config parameter by moving global config definition
-// from java subpackage to dexpreopt.
-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
 // class loader context for dex2oat, as the path to the jar in the Soong module may be unknown
 // at that time (Soong processes the jars in dependency order, which may be different from the
@@ -567,7 +572,7 @@
 	mctx, isModule := ctx.(android.ModuleContext)
 	if isModule {
 		config := GetGlobalConfig(ctx)
-		jars := AllSystemServerJars(ctx, config)
+		jars := config.AllSystemServerClasspathJars(ctx)
 		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
 			depIndex := jars.IndexOfJar(dep.Name())
 			if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 798d776..07e4fad 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -50,6 +50,15 @@
 		android.PathForOutput(ctx, fmt.Sprintf("%s/enforce_uses_libraries.status", name)))
 }
 
+func testPlatformSystemServerModuleConfig(ctx android.PathContext, name string) *ModuleConfig {
+	return createTestModuleConfig(
+		name,
+		fmt.Sprintf("/system/framework/%s.jar", 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,
@@ -181,6 +190,52 @@
 	android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
 }
 
+func TestDexPreoptStandaloneSystemServerJars(t *testing.T) {
+	config := android.TestConfig("out", nil, "", nil)
+	ctx := android.BuilderContextForTesting(config)
+	globalSoong := globalSoongConfigForTests()
+	global := GlobalConfigForTests(ctx)
+	module := testPlatformSystemServerModuleConfig(ctx, "service-A")
+
+	global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
+		[]string{"platform: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/service-A.odex"},
+		{android.PathForOutput(ctx, "service-A/dexpreopt/oat/arm/javalib.vdex"), "/system/framework/oat/arm/service-A.vdex"},
+	}
+
+	android.AssertStringEquals(t, "installs", wantInstalls.String(), rule.Installs().String())
+}
+
+func TestDexPreoptApexStandaloneSystemServerJars(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.ApexStandaloneSystemServerJars = 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/dexpreopt/testing.go b/dexpreopt/testing.go
index 2fba01a..47ae494 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -85,12 +85,12 @@
 // Prepares a test fixture by enabling dexpreopt, registering the fake_tool_binary module type and
 // using that to define the `dex2oatd` module.
 var PrepareForTestByEnablingDexpreopt = android.GroupFixturePreparers(
-	FixtureModifyGlobalConfig(func(*GlobalConfig) {}),
+	FixtureModifyGlobalConfig(func(android.PathContext, *GlobalConfig) {}),
 )
 
 // FixtureModifyGlobalConfig enables dexpreopt (unless modified by the mutator) and modifies the
 // configuration.
-func FixtureModifyGlobalConfig(configModifier func(dexpreoptConfig *GlobalConfig)) android.FixturePreparer {
+func FixtureModifyGlobalConfig(configModifier func(ctx android.PathContext, dexpreoptConfig *GlobalConfig)) android.FixturePreparer {
 	return android.FixtureModifyConfig(func(config android.Config) {
 		// Initialize the dexpreopt GlobalConfig to an empty structure. This has no effect if it has
 		// already been set.
@@ -100,48 +100,48 @@
 
 		// Retrieve the existing configuration and modify it.
 		dexpreoptConfig = GetGlobalConfig(pathCtx)
-		configModifier(dexpreoptConfig)
+		configModifier(pathCtx, dexpreoptConfig)
 	})
 }
 
 // FixtureSetArtBootJars enables dexpreopt and sets the ArtApexJars property.
 func FixtureSetArtBootJars(bootJars ...string) android.FixturePreparer {
-	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
 		dexpreoptConfig.ArtApexJars = android.CreateTestConfiguredJarList(bootJars)
 	})
 }
 
 // FixtureSetBootJars enables dexpreopt and sets the BootJars property.
 func FixtureSetBootJars(bootJars ...string) android.FixturePreparer {
-	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
 		dexpreoptConfig.BootJars = android.CreateTestConfiguredJarList(bootJars)
 	})
 }
 
 // FixtureSetApexBootJars sets the ApexBootJars property in the global config.
 func FixtureSetApexBootJars(bootJars ...string) android.FixturePreparer {
-	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
 		dexpreoptConfig.ApexBootJars = android.CreateTestConfiguredJarList(bootJars)
 	})
 }
 
 // FixtureSetStandaloneSystemServerJars sets the StandaloneSystemServerJars property.
 func FixtureSetStandaloneSystemServerJars(jars ...string) android.FixturePreparer {
-	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
 		dexpreoptConfig.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(jars)
 	})
 }
 
 // FixtureSetSystemServerJars sets the SystemServerJars property.
 func FixtureSetSystemServerJars(jars ...string) android.FixturePreparer {
-	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
 		dexpreoptConfig.SystemServerJars = android.CreateTestConfiguredJarList(jars)
 	})
 }
 
 // FixtureSetApexSystemServerJars sets the ApexSystemServerJars property in the global config.
 func FixtureSetApexSystemServerJars(jars ...string) android.FixturePreparer {
-	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
 		dexpreoptConfig.ApexSystemServerJars = android.CreateTestConfiguredJarList(jars)
 	})
 }
@@ -149,14 +149,28 @@
 // FixtureSetApexStandaloneSystemServerJars sets the ApexStandaloneSystemServerJars property in the
 // global config.
 func FixtureSetApexStandaloneSystemServerJars(jars ...string) android.FixturePreparer {
-	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
 		dexpreoptConfig.ApexStandaloneSystemServerJars = android.CreateTestConfiguredJarList(jars)
 	})
 }
 
 // FixtureSetPreoptWithUpdatableBcp sets the PreoptWithUpdatableBcp property in the global config.
 func FixtureSetPreoptWithUpdatableBcp(value bool) android.FixturePreparer {
-	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
 		dexpreoptConfig.PreoptWithUpdatableBcp = value
 	})
 }
+
+// FixtureSetBootImageProfiles sets the BootImageProfiles property in the global config.
+func FixtureSetBootImageProfiles(profiles ...string) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(ctx android.PathContext, dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.BootImageProfiles = android.PathsForSource(ctx, profiles)
+	})
+}
+
+// FixtureDisableGenerateProfile sets the DisableGenerateProfile property in the global config.
+func FixtureDisableGenerateProfile(disable bool) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.DisableGenerateProfile = disable
+	})
+}
diff --git a/docs/OWNERS b/docs/OWNERS
index d143317..2d71271 100644
--- a/docs/OWNERS
+++ b/docs/OWNERS
@@ -1 +1,3 @@
 per-file map_files.md = danalbert@google.com, enh@google.com, jiyong@google.com
+per-file native_code_coverage.md = pirama@google.com, srhines@google.com, allenhair@google.com
+per-file tidy.md = chh@google.com, srhines@google.com
diff --git a/docs/native_code_coverage.md b/docs/native_code_coverage.md
new file mode 100644
index 0000000..fabbfa0
--- /dev/null
+++ b/docs/native_code_coverage.md
@@ -0,0 +1,241 @@
+## Native Code Coverage for Android
+
+## Scope
+
+These instructions are for Android developers to collect and inspect code
+coverage for C++ and Rust code on the Android platform.
+
+## Building with Native Code Coverage Instrumentation
+
+Identify the paths where native code-coverage instrumentation should be enabled
+and set up the following environment variables.
+
+```
+    export CLANG_COVERAGE=true
+    export NATIVE_COVERAGE_PATHS="<paths-to-instrument-for-coverage>"
+```
+
+`NATIVE_COVERAGE_PATHS` should be a list of paths. Any Android.bp module defined
+under these paths is instrumented for code-coverage. E.g:
+
+```
+export NATIVE_COVERAGE_PATHS="external/libcxx system/core/adb"
+```
+
+### Additional Notes
+
+-   Native Code coverage is not supported for host modules or `Android.mk`
+    modules.
+-   `NATIVE_COVERAGE_PATHS="*"` enables coverage instrumentation for all paths.
+-   Set `native_coverage: false` blueprint property to always disable code
+    coverage instrumentation for a module. This is useful if this module has
+    issues when building or running with coverage.
+-   `NATIVE_COVERAGE_EXCLUDE_PATHS` can be set to exclude subdirs under
+    `NATIVE_COVERAGE_PATHS` from coverage instrumentation. E.g.
+    `NATIVE_COVERAGE_PATHS=frameworks/native
+    NATIVE_COVERAGE_PATHS=frameworks/native/vulkan` will instrument all native
+    code under `frameworks/native` except`frameworks/native/vulkan`.
+
+## Running Tests
+
+### Collecting Profiles
+
+When an instrumented program is run, the profiles are stored to the path and
+name specified in the `LLVM_PROFILE_FILE` environment variable. On Android
+coverage builds it is set to `/data/misc/trace/clang-%p-%20m.profraw`.
+
+*   `%`p is replaced by the pid of the process
+*   `%m` by the hash of the library/binary
+*   The `20` in`%20m` creates a pool of 20 profraw files and "online" profile
+    merging is used to merge coverage to profiles onto this pool.
+
+Reference:`LLVM_PROFILE_FILE` can include additional specifiers as described
+[here](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#running-the-instrumented-program).
+
+For this and following steps, use the `acov-llvm.py` script:
+`$ANDROID_BUILD_TOP/development/scripts/acov-llvm.py`.
+
+There may be profiles in `/data/misc/trace` collected before the test is run.
+Clear this data before running the test.
+
+```
+    # Clear any coverage that's already written to /data/misc/trace
+    # and reset coverage for all daemons.
+    <host>$ acov-llvm.py clean-device
+
+    # Run the test.  The exact command depends on the nature of the test.
+    <device>$ /data/local/tmp/$MY_TEST
+```
+
+For tests that exercise a daemon/service running in another process, write out
+the coverage for those processes as well.
+
+```
+    # Flush coverage of all daemons/processes running on the device.
+    <host>$ acov-llvm.py flush
+
+    # Flush coverage for a particular daemon, say adbd.
+    <host>$ acov-llvm.py flush adbd
+```
+
+## Viewing Coverage Data (acov-llvm.py)
+
+To post-process and view coverage information we use the `acov-llvm.py report`
+command. It invokes two LLVM utilities `llvm-profdata` and `llvm-cov`. An
+advanced user can manually invoke these utilities for fine-grained control. This
+is discussed [below](#viewing-coverage-data-manual).
+
+To generate coverage report need the following parameters. These are dependent
+on the test/module:
+
+1.  One or more binaries and shared libraries from which coverage was collected.
+    E.g.:
+
+    1.  ART mainline module contains a few libraries such as `libart.so`,
+        `libart-compiler.so`.
+    2.  Bionic tests exercise code in `libc.so` and `libm.so`.
+
+    We need the *unstripped* copies of these binaries. Source information
+    included in the debuginfo is used to process the coverage data.
+
+2.  One or more source directories under `$ANDROID_BUILD_TOP` for which coverage
+    needs to be reported.
+
+Invoke the report subcommand of acov-llvm.py to produce a html coverage summary:
+
+```
+    $ acov-llvm.py report \
+        -s <one-or-more-source-paths-in-$ANDROID_BUILD_TOP \
+        -b <one-or-more-(unstripped)-binaries-in-$OUT>
+```
+
+E.g.:
+
+```
+    $ acov-llvm.py report \
+        -s bionic \
+        -b \
+        $OUT/symbols/apex/com.android.runtime/lib/bionic/libc.so \
+        $OUT/symbols/apex/com.android.runtime/lib/bionic/libm.so
+```
+
+The script will produce a report in a temporary directory under
+`$ANDROID_BUILD_TOP`. It'll produce a log as below:
+
+```
+    generating coverage report in covreport-xxxxxx
+```
+
+A html report would be generated under `covreport-xxxxxx/html`.
+
+## Viewing Coverage Data (manual)
+
+`acov-llvm.py report` does a few operations under the hood which we can also
+manually invoke for flexibility.
+
+### Post-processing Coverage Files
+
+Fetch coverage files from the device and post-process them to a `.profdata` file
+as follows:
+
+```
+    # Fetch the coverage data from the device.
+    <host>$ cd coverage_data
+    <host>$ adb pull /data/misc/trace/ $TRACE_DIR_HOST
+
+    # Convert from .profraw format to the .profdata format.
+    <host>$ llvm-profdata merge --output=$MY_TEST.profdata \
+    $TRACE_DIR_HOST/clang-*.profraw
+```
+
+For added specificity, restrict the above command to just the <PID>s of the
+daemon or test processes of interest.
+
+```
+    <host>$ llvm-profdata merge --output=$MY_TEST.profdata \
+    $MY_TEST.profraw \
+    trace/clang-<pid1>.profraw trace/clang-<pid2>.profraw ...
+```
+
+### Generating Coverage report
+
+Documentation on Clang source-instrumentation-based coverage is available
+[here](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#creating-coverage-reports).
+The `llvm-cov` utility is used to show coverage from a `.profdata` file. The
+documentation for commonly used `llvm-cov` command-line arguments is available
+[here](https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report). (Try
+`llvm-cov show --help` for a complete list).
+
+#### `show` subcommand
+
+The `show` command displays the function and line coverage for each source file
+in the binary.
+
+```
+    <host>$ llvm-cov show \
+        --show-region-summary=false
+        --format=html --output-dir=coverage-html \
+        --instr-profile=$MY_TEST.profdata \
+        $MY_BIN \
+```
+
+*   In the above command, `$MY_BIN` should be the unstripped binary (i.e. with
+    debuginfo) since `llvm-cov` reads some debuginfo to process the coverage
+    data.
+
+    E.g.:
+
+    ~~~
+    ```
+    <host>$ llvm-cov report \
+        --instr-profile=adbd.profdata \
+        $LOCATION_OF_UNSTRIPPED_ADBD/adbd \
+        --show-region-summary=false
+    ```
+    ~~~
+
+*   The `-ignore-filename-regex=<regex>` option can be used to ignore files that
+    are not of interest. E.g: `-ignore-filename-regex="external/*"`
+
+*   Use the `--object=<BIN>` argument to specify additional binaries and shared
+    libraries whose coverage is included in this profdata. See the earlier
+    [section](#viewing-coverage-data-acov-llvm-py) for examples where more than
+    one binary may need to be used.
+
+    E.g., the following command is used for `bionic-unit-tests`, which tests
+    both `libc.so` and `libm.so`:
+
+    ~~~
+    ```
+    <host>$ llvm-cov report \
+        --instr-profile=bionic.profdata \
+        $OUT/.../libc.so \
+        --object=$OUT/.../libm.so
+    ```
+    ~~~
+
+*   `llvm-cov` also takes positional SOURCES argument to consider/display only
+    particular paths of interest. E.g:
+
+    ~~~
+    ```
+    <host>$ llvm-cov report \
+        --instr-profile=adbd.profdata \
+        $LOCATION_OF_ADBD/adbd \
+        --show-region-summary=false \
+        /proc/self/cwd/system/core/adb
+    ```
+    ~~~
+
+Note that the paths for the sources need to be prepended with
+'`/proc/self/cwd/`'. This is because Android C/C++ compilations run with
+`PWD=/proc/self/cwd` and consequently the source names are recorded with that
+prefix. Alternatively, the
+[`--path-equivalence`](https://llvm.org/docs/CommandGuide/llvm-cov.html#cmdoption-llvm-cov-show-path-equivalence)
+option to `llvm-cov` can be used.
+
+#### `report` subcommand
+
+The [`report`](https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report)
+subcommand summarizes the percentage of covered lines to the console. It takes
+options similar to the `show` subcommand.
diff --git a/docs/tidy.md b/docs/tidy.md
new file mode 100644
index 0000000..890c3a0
--- /dev/null
+++ b/docs/tidy.md
@@ -0,0 +1,254 @@
+# Clang-Tidy Rules and Checks
+
+Android C/C++ source files can be checked by clang-tidy for issues like
+coding style, error-prone/performance patterns, and static flow analysis.
+See the official
+[clang-tidy document](https://clang.llvm.org/extra/clang-tidy)
+and list of
+[clang-tidy checks](https://clang.llvm.org/extra/clang-tidy/checks/list.html).
+
+## Global defaults for Android builds
+
+The simplest way to enable clang-tidy checks is
+to set environment variable `WITH_TIDY`.
+```
+$ WITH_TIDY=1 make
+```
+
+This will turn on the global default to run clang-tidy for every required
+C/C++ source file compilation. The global default clang-tidy checks
+do not include time-consuming static analyzer checks. To enable those
+checks, set the `CLANG_ANALYZER_CHECKS` variable.
+```
+$ WITH_TIDY=1 CLANG_ANALYZER_CHECKS=1 make
+```
+
+The default global clang-tidy checks and flags are defined in
+[build/soong/cc/config/tidy.go](https://android.googlesource.com/platform/build/soong/+/refs/heads/master/cc/config/tidy.go).
+
+
+## Module clang-tidy properties
+
+The global default can be overwritten by module properties in Android.bp.
+
+### `tidy` and `tidy_checks`
+
+For example, in
+[system/bpf/Android.bp](https://android.googlesource.com/platform/system/bpf/+/refs/heads/master/Android.bp),
+clang-tidy is enabled explicitly and with a different check list:
+```
+cc_defaults {
+    name: "bpf_defaults",
+    // snipped
+    tidy: true,
+    tidy_checks: [
+        "android-*",
+        "cert-*",
+        "-cert-err34-c",
+        "clang-analyzer-security*",
+        // Disabling due to many unavoidable warnings from POSIX API usage.
+        "-google-runtime-int",
+    ],
+}
+```
+That means in normal builds, even without `WITH_TIDY=1`,
+the modules that use `bpf_defaults` will run clang-tidy
+over C/C++ source files with the given `tidy_checks`.
+Note that `clang-analyzer-security*` is included in `tidy_checks`
+but not all `clang-analyzer-*` checks. Check `cert-err34-c` is
+disabled, although `cert-*` is selected.
+
+Some modules might want to disable clang-tidy even when
+environment variable `WITH_TIDY=1` is set.
+Examples can be found in
+[system/netd/tests/Android.bp](https://android.googlesource.com/platform/system/netd/+/refs/heads/master/tests/Android.bp)
+```
+cc_test {
+    name: "netd_integration_test",
+    // snipped
+    defaults: ["netd_defaults"],
+    tidy: false,  // cuts test build time by almost 1 minute
+```
+and in
+[bionic/tests/Android.bp](https://android.googlesource.com/platform/bionic/+/refs/heads/master/tests/Android.bp).
+```
+cc_test_library {
+    name: "fortify_disabled_for_tidy",
+    // snipped
+    srcs: ["clang_fortify_tests.cpp"],
+    tidy: false,
+}
+```
+
+### `tidy_checks_as_errors`
+
+The global tidy checks are enabled as warnings.
+If a C/C++ module wants to be free of certain clang-tidy warnings,
+it can chose those checks to be treated as errors.
+For example
+[system/core/libsysutils/Android.bp](https://android.googlesource.com/platform/system/core/+/refs/heads/master/libsysutils/Android.bp)
+has enabled clang-tidy explicitly, selected its own tidy checks,
+and set three groups of tidy checks as errors:
+```
+cc_library {
+    name: "libsysutils",
+    // snipped
+    tidy: true,
+    tidy_checks: [
+        "-*",
+        "cert-*",
+        "clang-analyzer-security*",
+        "android-*",
+    ],
+    tidy_checks_as_errors: [
+        "cert-*",
+        "clang-analyzer-security*",
+        "android-*",
+    ],
+    // snipped
+}
+```
+
+### `tidy_flags` and `tidy_disabled_srcs`
+
+Extra clang-tidy flags can be passed with the `tidy_flags` property.
+
+Some Android modules use the `tidy_flags` to pass "-warnings-as-errors="
+to clang-tidy. This usage should now be replaced with the
+`tidy_checks_as_errors` property.
+
+Some other tidy flags examples are `-format-style=` and `-header-filter=`
+For example, in
+[art/odrefresh/Android.bp](https://android.googlesource.com/platform/art/+/refs/heads/master/odrefresh/Android.bp),
+we found
+```
+cc_defaults {
+    name: "odrefresh-defaults",
+    srcs: [
+        "odrefresh.cc",
+        "odr_common.cc",
+        "odr_compilation_log.cc",
+        "odr_fs_utils.cc",
+        "odr_metrics.cc",
+        "odr_metrics_record.cc",
+    ],
+    // snipped
+    generated_sources: [
+        "apex-info-list-tinyxml",
+        "art-apex-cache-info",
+        "art-odrefresh-operator-srcs",
+    ],
+    // snipped
+    tidy: true,
+    tidy_disabled_srcs: [":art-apex-cache-info"],
+    tidy_flags: [
+        "-format-style=file",
+        "-header-filter=(art/odrefresh/|system/apex/)",
+    ],
+}
+```
+That means all modules with the `odrefresh-defaults` will
+have clang-tidy enabled, but not for generated source
+files in `art-apex-cache-info`.
+The clang-tidy is called with extra flags to specify the
+format-style and header-filter.
+
+Note that the globally set default for header-filter is to
+include only the module directory. So, the default clang-tidy
+warnings for `art/odrefresh` modules will include source files
+under that directory. Now `odrefresh-defaults` is interested
+in seeing warnings from both `art/odrefresh/` and `system/apex/`
+and it redefines `-header-filter` in its `tidy_flags`.
+
+
+## Phony tidy-* targets
+
+### The tidy-*directory* targets
+
+Setting `WITH_TIDY=1` is easy to enable clang-tidy globally for any build.
+However, it adds extra compilation time.
+
+For developers focusing on just one directory, they only want to compile
+their files with clang-tidy and wish to build other Android components as
+fast as possible. Changing the `WITH_TIDY=1` variable setting is also expensive
+since the build.ninja file will be regenerated due to any such variable change.
+
+To manually select only some directories or modules to compile with clang-tidy,
+do not set the `WITH_TIDY=1` variable, but use the special `tidy-<directory>`
+phony target. For example, a person working on `system/libbase` can build
+Android quickly with
+```
+unset WITH_TIDY # Optional, not if you haven't set WITH_TIDY
+make droid tidy-system-libbase
+```
+
+For any directory `d1/d2/d3`, a phony target tidy-d1-d2-d3 is generated
+if there is any C/C++ source file under `d1/d2/d3`.
+
+Note that with `make droid tidy-system-libbase`, some C/C++ files
+that are not needed by the `droid` target will be passed to clang-tidy
+if they are under `system/libbase`. This is like a `checkbuild`
+under `system/libbase` to include all modules, but only C/C++
+files of those modules are compiled with clang-tidy.
+
+### The tidy-soong target
+
+A special `tidy-soong` target is defined to include all C/C++
+source files in *all* directories. This phony target is sometimes
+used to test if all source files compile with a new clang-tidy release.
+
+### The tidy-*_subset targets
+
+A *subset* of each tidy-* phony target is defined to reduce test time.
+Since any Android module, a C/C++ library or binary, can be built
+for many different *variants*, one C/C++ source file is usually
+compiled multiple times with different compilation flags.
+Many of such *variant* flags have little or no effect on clang-tidy
+checks. To reduce clang-tidy check time, a *subset* target like
+`tidy-soong_subset` or `tidy-system-libbase_subset` is generated
+to include only a subset, the first variant, of each module in
+the directory.
+
+Hence, for C/C++ source code quality, instead of a long
+"make checkbuild", we can use "make tidy-soong_subset".
+
+
+## Limit clang-tidy runtime
+
+Some Android modules have large files that take a long time to compile
+with clang-tidy, with or without the clang-analyzer checks.
+To limit clang-tidy time, an environment variable can be set as
+```base
+WITH_TIDY=1 TIDY_TIMEOUT=90 make
+```
+This 90-second limit is actually the default time limit
+in several Android continuous builds where `WITH_TIDY=1` and
+`CLANG_ANALYZER_CHECKS=1` are set.
+
+Similar to `tidy_disabled_srcs` a `tidy_timeout_srcs` list
+can be used to include all source files that took too much time to compile
+with clang-tidy. Files listed in `tidy_timeout_srcs` will not
+be compiled by clang-tidy when `TIDY_TIMEOUT` is defined.
+This can save global build time, when it is necessary to set some
+time limit globally to finish in an acceptable time.
+For developers who want to find all clang-tidy warnings and
+are willing to spend more time on all files in a project,
+they should not define `TIDY_TIMEOUT` and build only the wanted project directories.
+
+## Capabilities for Android.bp and Android.mk
+
+Some of the previously mentioned features are defined only
+for modules in Android.bp files, not for Android.mk modules yet.
+
+* The global `WITH_TIDY=1` variable will enable clang-tidy for all C/C++
+  modules in Android.bp or Android.mk files.
+
+* The global `TIDY_TIMEOUT` variable is recognized by Android prebuilt
+  clang-tidy, so it should work for any clang-tidy invocation.
+
+* The clang-tidy module level properties are defined for Android.bp modules.
+  For Android.mk modules, old `LOCAL_TIDY`, `LOCAL_TIDY_CHECKS`,
+  `LOCAL_TIDY_FLAGS` work similarly, but it would be better to convert
+  those modules to use Android.bp files.
+
+* The `tidy-*` phony targets are only generated for Android.bp modules.
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 2865ffa..a142833 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -54,6 +54,7 @@
 	ctx.RegisterModuleType("prebuilt_etc", PrebuiltEtcFactory)
 	ctx.RegisterModuleType("prebuilt_etc_host", PrebuiltEtcHostFactory)
 	ctx.RegisterModuleType("prebuilt_root", PrebuiltRootFactory)
+	ctx.RegisterModuleType("prebuilt_root_host", PrebuiltRootHostFactory)
 	ctx.RegisterModuleType("prebuilt_usr_share", PrebuiltUserShareFactory)
 	ctx.RegisterModuleType("prebuilt_usr_share_host", PrebuiltUserShareHostFactory)
 	ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
@@ -63,7 +64,6 @@
 
 	ctx.RegisterModuleType("prebuilt_defaults", defaultsFactory)
 
-	android.RegisterBp2BuildMutator("prebuilt_etc", PrebuiltEtcBp2Build)
 }
 
 var PrepareForTestWithPrebuiltEtc = android.FixtureRegisterWithContext(RegisterPrebuiltEtcBuildComponents)
@@ -377,7 +377,7 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_TAGS", "optional")
-				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
 				if len(p.properties.Symlinks) > 0 {
 					entries.AddStrings("LOCAL_MODULE_SYMLINKS", p.properties.Symlinks...)
@@ -440,6 +440,7 @@
 	// This module is host-only
 	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+	android.InitBazelModule(module)
 	return module
 }
 
@@ -454,6 +455,17 @@
 	return module
 }
 
+// prebuilt_root_host is for a host prebuilt artifact that is installed in $(HOST_OUT)/<sub_dir>
+// directory.
+func PrebuiltRootHostFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, ".")
+	// This module is host-only
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+	return module
+}
+
 // prebuilt_usr_share is for a prebuilt artifact that is installed in
 // <partition>/usr/share/<sub_dir> directory.
 func PrebuiltUserShareFactory() android.Module {
@@ -663,20 +675,14 @@
 	Installable bazel.BoolAttribute
 }
 
-func PrebuiltEtcBp2Build(ctx android.TopDownMutatorContext) {
-	module, ok := ctx.Module().(*PrebuiltEtc)
-	if !ok {
-		// Not an prebuilt_etc
-		return
-	}
-	if !module.ConvertWithBp2build(ctx) {
-		return
-	}
-	if ctx.ModuleType() != "prebuilt_etc" {
+// ConvertWithBp2build performs bp2build conversion of PrebuiltEtc
+func (p *PrebuiltEtc) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	// All prebuilt_* modules are PrebuiltEtc, but at this time, we only convert prebuilt_etc modules.
+	if p.installDirBase != "etc" {
 		return
 	}
 
-	prebuiltEtcBp2BuildInternal(ctx, module)
+	prebuiltEtcBp2BuildInternal(ctx, p)
 }
 
 func prebuiltEtcBp2BuildInternal(ctx android.TopDownMutatorContext, module *PrebuiltEtc) {
@@ -688,7 +694,8 @@
 				continue
 			}
 			if props.Src != nil {
-				srcLabelAttribute.SetSelectValue(axis, config, android.BazelLabelForModuleSrcSingle(ctx, *props.Src))
+				label := android.BazelLabelForModuleSrcSingle(ctx, *props.Src)
+				srcLabelAttribute.SetSelectValue(axis, config, label)
 			}
 		}
 	}
diff --git a/etc/snapshot_etc.go b/etc/snapshot_etc.go
index 9a25d5a..b54a8a6 100644
--- a/etc/snapshot_etc.go
+++ b/etc/snapshot_etc.go
@@ -128,7 +128,7 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_TAGS", "optional")
-				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
 			},
 		},
diff --git a/example_config.json b/example_config.json
new file mode 100644
index 0000000..7489840
--- /dev/null
+++ b/example_config.json
@@ -0,0 +1,6 @@
+{
+    "env": {
+	"ENV_VAR_1": "Value1",
+	"ENV_VAR_2": "Value2"
+    }
+}
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 73d807d..33beb37 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -145,12 +145,10 @@
 	}
 
 	dtbName := proptools.String(b.properties.Dtb_prebuilt)
-	if dtbName == "" {
-		ctx.PropertyErrorf("dtb_prebuilt", "must be set")
-		return output
+	if dtbName != "" {
+		dtb := android.PathForModuleSrc(ctx, dtbName)
+		cmd.FlagWithInput("--dtb ", dtb)
 	}
-	dtb := android.PathForModuleSrc(ctx, dtbName)
-	cmd.FlagWithInput("--dtb ", dtb)
 
 	cmdline := strings.Join(b.properties.Cmdline, " ")
 	if cmdline != "" {
@@ -178,20 +176,18 @@
 	cmd.FlagWithArg("--header_version ", headerVersion)
 
 	ramdiskName := proptools.String(b.properties.Ramdisk_module)
-	if ramdiskName == "" {
-		ctx.PropertyErrorf("ramdisk_module", "must be set")
-		return output
-	}
-	ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep)
-	if filesystem, ok := ramdisk.(*filesystem); ok {
-		flag := "--ramdisk "
-		if vendor {
-			flag = "--vendor_ramdisk "
+	if ramdiskName != "" {
+		ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep)
+		if filesystem, ok := ramdisk.(*filesystem); ok {
+			flag := "--ramdisk "
+			if vendor {
+				flag = "--vendor_ramdisk "
+			}
+			cmd.FlagWithInput(flag, filesystem.OutputPath())
+		} else {
+			ctx.PropertyErrorf("ramdisk", "%q is not android_filesystem module", ramdisk.Name())
+			return output
 		}
-		cmd.FlagWithInput(flag, filesystem.OutputPath())
-	} else {
-		ctx.PropertyErrorf("ramdisk", "%q is not android_filesystem module", ramdisk.Name())
-		return output
 	}
 
 	bootconfig := proptools.String(b.properties.Bootconfig)
@@ -267,7 +263,7 @@
 		OutputFile: android.OptionalPathForPath(b.output),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", b.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_PATH", b.installDir.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", b.installFileName())
 			},
 		},
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index b2caa51..0796258 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -394,7 +394,7 @@
 		OutputFile: android.OptionalPathForPath(f.output),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", f.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_PATH", f.installDir.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
 			},
 		},
diff --git a/filesystem/logical_partition.go b/filesystem/logical_partition.go
index 739e609..e2f7d7b 100644
--- a/filesystem/logical_partition.go
+++ b/filesystem/logical_partition.go
@@ -215,7 +215,7 @@
 		OutputFile: android.OptionalPathForPath(l.output),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", l.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_PATH", l.installDir.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.installFileName())
 			},
 		},
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index 3f16c0d..63e0aba 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -247,7 +247,7 @@
 		OutputFile: android.OptionalPathForPath(v.output),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", v.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_PATH", v.installDir.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", v.installFileName())
 			},
 		},
diff --git a/finder/finder.go b/finder/finder.go
index 5413fa6..b4834b1 100644
--- a/finder/finder.go
+++ b/finder/finder.go
@@ -847,6 +847,7 @@
 	if err != nil {
 		return errors.New("No data to load from database\n")
 	}
+	defer reader.Close()
 	bufferedReader := bufio.NewReader(reader)
 	if !f.validateCacheHeader(bufferedReader) {
 		return errors.New("Cache header does not match")
diff --git a/finder/fs/test.go b/finder/fs/test.go
index cb2140e..ed981fd 100644
--- a/finder/fs/test.go
+++ b/finder/fs/test.go
@@ -74,6 +74,7 @@
 	if err != nil {
 		t.Fatalf(err.Error())
 	}
+	defer reader.Close()
 	bytes, err := ioutil.ReadAll(reader)
 	if err != nil {
 		t.Fatal(err.Error())
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 8861d1b..89f8187 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -31,6 +31,7 @@
 const (
 	Cc   Lang = ""
 	Rust Lang = "rust"
+	Java Lang = "java"
 )
 
 var BoolDefault = proptools.BoolDefault
@@ -220,6 +221,9 @@
 		if lang == Rust {
 			zipFileName = "fuzz-rust-" + hostOrTarget + "-" + arch + ".zip"
 		}
+		if lang == Java {
+			zipFileName = "fuzz-java-" + hostOrTarget + "-" + arch + ".zip"
+		}
 		outputFile := android.PathForOutput(ctx, zipFileName)
 
 		s.Packages = append(s.Packages, outputFile)
diff --git a/genrule/genrule.go b/genrule/genrule.go
index c9bf958..8282426 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -67,13 +67,6 @@
 	ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel()
 	})
-
-	android.RegisterBp2BuildMutator("genrule", GenruleBp2Build)
-	android.RegisterBp2BuildMutator("cc_genrule", CcGenruleBp2Build)
-}
-
-func RegisterGenruleBp2BuildDeps(ctx android.RegisterMutatorsContext) {
-	ctx.BottomUp("genrule_tool_deps", toolDepsMutator)
 }
 
 var (
@@ -110,8 +103,19 @@
 
 type hostToolDependencyTag struct {
 	blueprint.BaseDependencyTag
+	android.LicenseAnnotationToolchainDependencyTag
 	label string
 }
+
+func (t hostToolDependencyTag) AllowDisabledModuleDependency(target android.Module) bool {
+	// Allow depending on a disabled module if it's replaced by a prebuilt
+	// counterpart. We get the prebuilt through android.PrebuiltGetPreferred in
+	// GenerateAndroidBuildActions.
+	return target.IsReplacedByPrebuilt()
+}
+
+var _ android.AllowDisabledModuleDependency = (*hostToolDependencyTag)(nil)
+
 type generatorProperties struct {
 	// The command to run on one or more input files. Cmd supports substitution of a few variables.
 	//
@@ -290,7 +294,7 @@
 		if _, exists := locationLabels[label]; !exists {
 			locationLabels[label] = loc
 		} else {
-			ctx.ModuleErrorf("multiple labels for %q, %q and %q",
+			ctx.ModuleErrorf("multiple locations for label %q: %q and %q (do you have duplicate srcs entries?)",
 				label, locationLabels[label], loc)
 		}
 	}
@@ -304,6 +308,12 @@
 			switch tag := ctx.OtherModuleDependencyTag(module).(type) {
 			case hostToolDependencyTag:
 				tool := ctx.OtherModuleName(module)
+				if m, ok := module.(android.Module); ok {
+					// Necessary to retrieve any prebuilt replacement for the tool, since
+					// toolDepsMutator runs too late for the prebuilt mutators to have
+					// replaced the dependency.
+					module = android.PrebuiltGetPreferred(ctx, m)
+				}
 
 				switch t := module.(type) {
 				case android.HostToolProvider:
@@ -832,38 +842,8 @@
 	Cmd   string
 }
 
-// CcGenruleBp2Build is for cc_genrule.
-func CcGenruleBp2Build(ctx android.TopDownMutatorContext) {
-	m, ok := ctx.Module().(*Module)
-	if !ok || !m.ConvertWithBp2build(ctx) {
-		return
-	}
-
-	if ctx.ModuleType() != "cc_genrule" {
-		// Not a cc_genrule.
-		return
-	}
-
-	genruleBp2Build(ctx)
-}
-
-// GenruleBp2Build is used for genrule.
-func GenruleBp2Build(ctx android.TopDownMutatorContext) {
-	m, ok := ctx.Module().(*Module)
-	if !ok || !m.ConvertWithBp2build(ctx) {
-		return
-	}
-
-	if ctx.ModuleType() != "genrule" {
-		// Not a regular genrule.
-		return
-	}
-
-	genruleBp2Build(ctx)
-}
-
-func genruleBp2Build(ctx android.TopDownMutatorContext) {
-	m, _ := ctx.Module().(*Module)
+// ConvertWithBp2build converts a Soong module -> Bazel target.
+func (m *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
 	// Bazel only has the "tools" attribute.
 	tools_prop := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
 	tool_files_prop := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
@@ -882,7 +862,7 @@
 		cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
 		cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
 		genDir := "$(GENDIR)"
-		if ctx.ModuleType() == "cc_genrule" {
+		if t := ctx.ModuleType(); t == "cc_genrule" || t == "java_genrule" || t == "java_genrule_host" {
 			genDir = "$(RULEDIR)"
 		}
 		cmd = strings.Replace(cmd, "$(genDir)", genDir, -1)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 714d2f8..04c97fd 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -34,7 +34,9 @@
 	android.PrepareForTestWithFilegroup,
 	PrepareForTestWithGenRuleBuildComponents,
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		android.RegisterPrebuiltMutators(ctx)
 		ctx.RegisterModuleType("tool", toolFactory)
+		ctx.RegisterModuleType("prebuilt_tool", prebuiltToolFactory)
 		ctx.RegisterModuleType("output", outputProducerFactory)
 		ctx.RegisterModuleType("use_source", useSourceFactory)
 	}),
@@ -720,6 +722,69 @@
 		result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
 }
 
+func TestPrebuiltTool(t *testing.T) {
+	testcases := []struct {
+		name             string
+		bp               string
+		expectedToolName string
+	}{
+		{
+			name: "source only",
+			bp: `
+				tool { name: "tool" }
+			`,
+			expectedToolName: "bin/tool",
+		},
+		{
+			name: "prebuilt only",
+			bp: `
+				prebuilt_tool { name: "tool" }
+			`,
+			expectedToolName: "prebuilt_bin/tool",
+		},
+		{
+			name: "source preferred",
+			bp: `
+				tool { name: "tool" }
+				prebuilt_tool { name: "tool" }
+			`,
+			expectedToolName: "bin/tool",
+		},
+		{
+			name: "prebuilt preferred",
+			bp: `
+				tool { name: "tool" }
+				prebuilt_tool { name: "tool", prefer: true }
+			`,
+			expectedToolName: "prebuilt_bin/prebuilt_tool",
+		},
+		{
+			name: "source disabled",
+			bp: `
+				tool { name: "tool", enabled: false }
+				prebuilt_tool { name: "tool" }
+      `,
+			expectedToolName: "prebuilt_bin/prebuilt_tool",
+		},
+	}
+
+	for _, test := range testcases {
+		t.Run(test.name, func(t *testing.T) {
+			result := prepareForGenRuleTest.RunTestWithBp(t, test.bp+`
+				genrule {
+					name: "gen",
+					tools: ["tool"],
+					out: ["foo"],
+					cmd: "$(location tool)",
+				}
+			`)
+			gen := result.Module("gen", "").(*Module)
+			expectedCmd := "__SBOX_SANDBOX_DIR__/tools/out/" + test.expectedToolName
+			android.AssertStringEquals(t, "command", expectedCmd, gen.rawCommands[0])
+		})
+	}
+}
+
 func TestGenruleWithBazel(t *testing.T) {
 	bp := `
 		genrule {
@@ -764,7 +829,33 @@
 	return android.OptionalPathForPath(t.outputFile)
 }
 
+type prebuiltTestTool struct {
+	android.ModuleBase
+	prebuilt android.Prebuilt
+	testTool
+}
+
+func (p *prebuiltTestTool) Name() string {
+	return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+func (p *prebuiltTestTool) Prebuilt() *android.Prebuilt {
+	return &p.prebuilt
+}
+
+func (t *prebuiltTestTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "prebuilt_bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
+}
+
+func prebuiltToolFactory() android.Module {
+	module := &prebuiltTestTool{}
+	android.InitPrebuiltModuleWithoutSrcs(module)
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	return module
+}
+
 var _ android.HostToolProvider = (*testTool)(nil)
+var _ android.HostToolProvider = (*prebuiltTestTool)(nil)
 
 type testOutputProducer struct {
 	android.ModuleBase
diff --git a/java/Android.bp b/java/Android.bp
index 8835b44..c062941 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -44,6 +44,7 @@
         "dexpreopt_config.go",
         "droiddoc.go",
         "droidstubs.go",
+        "fuzz.go",
         "gen.go",
         "genrule.go",
         "hiddenapi.go",
diff --git a/java/OWNERS b/java/OWNERS
index 5242712..5b71b1e 100644
--- a/java/OWNERS
+++ b/java/OWNERS
@@ -1 +1,4 @@
 per-file dexpreopt*.go = ngeoffray@google.com,calin@google.com,skvadrik@google.com
+
+# For metalava team to disable lint checks in platform
+per-file droidstubs.go = aurimas@google.com,emberrose@google.com,sjgilbert@google.com
\ No newline at end of file
diff --git a/java/aar.go b/java/aar.go
index 13390db..8e10253 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -267,18 +267,29 @@
 	})
 
 func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkContext,
-	classLoaderContexts dexpreopt.ClassLoaderContextMap, extraLinkFlags ...string) {
+	classLoaderContexts dexpreopt.ClassLoaderContextMap, excludedLibs []string,
+	extraLinkFlags ...string) {
 
 	transitiveStaticLibs, transitiveStaticLibManifests, staticRRODirs, assetPackages, libDeps, libFlags :=
 		aaptLibs(ctx, sdkContext, classLoaderContexts)
 
+	// Exclude any libraries from the supplied list.
+	classLoaderContexts = classLoaderContexts.ExcludeLibs(excludedLibs)
+
 	// App manifest file
 	manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
 	manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile)
 
-	manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, classLoaderContexts,
-		a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode,
-		a.LoggingParent)
+	manifestPath := ManifestFixer(ctx, manifestSrcPath, ManifestFixerParams{
+		SdkContext:            sdkContext,
+		ClassLoaderContexts:   classLoaderContexts,
+		IsLibrary:             a.isLibrary,
+		UseEmbeddedNativeLibs: a.useEmbeddedNativeLibs,
+		UsesNonSdkApis:        a.usesNonSdkApis,
+		UseEmbeddedDex:        a.useEmbeddedDex,
+		HasNoCode:             a.hasNoCode,
+		LoggingParent:         a.LoggingParent,
+	})
 
 	// Add additional manifest files to transitive manifests.
 	additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests)
@@ -520,7 +531,7 @@
 func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.aapt.isLibrary = true
 	a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
-	a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts)
+	a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, nil)
 
 	a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
 
@@ -616,6 +627,7 @@
 	exportPackage         android.WritablePath
 	extraAaptPackagesFile android.WritablePath
 	manifest              android.WritablePath
+	assetsPackage         android.WritablePath
 
 	exportedStaticPackages android.Paths
 
@@ -686,9 +698,8 @@
 	return android.Paths{a.manifest}
 }
 
-// TODO(jungjw): Decide whether we want to implement this.
 func (a *AARImport) ExportedAssets() android.OptionalPath {
-	return android.OptionalPath{}
+	return android.OptionalPathForPath(a.assetsPackage)
 }
 
 // RRO enforcement is not available on aar_import since its RRO dirs are not
@@ -732,10 +743,11 @@
 	blueprint.RuleParams{
 		Command: `rm -rf $outDir && mkdir -p $outDir && ` +
 			`unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out && ` +
+			`${config.Zip2ZipCmd} -i $in -o $assetsPackage 'assets/**/*' && ` +
 			`${config.MergeZipsCmd} $combinedClassesJar $$(ls $outDir/classes.jar 2> /dev/null) $$(ls $outDir/libs/*.jar 2> /dev/null)`,
-		CommandDeps: []string{"${config.MergeZipsCmd}"},
+		CommandDeps: []string{"${config.MergeZipsCmd}", "${config.Zip2ZipCmd}"},
 	},
-	"outDir", "combinedClassesJar")
+	"outDir", "combinedClassesJar", "assetsPackage")
 
 func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if len(a.properties.Aars) != 1 {
@@ -761,15 +773,17 @@
 	a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar")
 	a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
 	a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
+	a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip")
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        unzipAAR,
 		Input:       a.aarPath,
-		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest},
+		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest, a.assetsPackage},
 		Description: "unzip AAR",
 		Args: map[string]string{
 			"outDir":             extractedAARDir.String(),
 			"combinedClassesJar": a.classpathFile.String(),
+			"assetsPackage":      a.assetsPackage.String(),
 		},
 	})
 
@@ -812,6 +826,19 @@
 	aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, rTxt, a.extraAaptPackagesFile,
 		linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil)
 
+	// Merge this import's assets with its dependencies' assets (if there are any).
+	if len(transitiveAssets) > 0 {
+		mergedAssets := android.PathForModuleOut(ctx, "merged-assets.zip")
+		inputZips := append(android.Paths{a.assetsPackage}, transitiveAssets...)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        mergeAssetsRule,
+			Inputs:      inputZips,
+			Output:      mergedAssets,
+			Description: "merge assets from dependencies and self",
+		})
+		a.assetsPackage = mergedAssets
+	}
+
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(a.classpathFile),
 		ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile),
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 38065f1..7772b70 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -27,13 +28,10 @@
 var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer",
 	blueprint.RuleParams{
 		Command: `${config.ManifestFixerCmd} ` +
-			`--minSdkVersion ${minSdkVersion} ` +
-			`--targetSdkVersion ${targetSdkVersion} ` +
-			`--raise-min-sdk-version ` +
 			`$args $in $out`,
 		CommandDeps: []string{"${config.ManifestFixerCmd}"},
 	},
-	"minSdkVersion", "targetSdkVersion", "args")
+	"args")
 
 var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger",
 	blueprint.RuleParams{
@@ -42,86 +40,124 @@
 	},
 	"args", "libs")
 
-// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
-func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext android.SdkContext,
-	classLoaderContexts dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis,
-	useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path {
+// targetSdkVersion for manifest_fixer
+// When TARGET_BUILD_APPS is not empty, this method returns 10000 for modules targeting an unreleased SDK
+// This enables release builds (that run with TARGET_BUILD_APPS=[val...]) to target APIs that have not yet been finalized as part of an SDK
+func targetSdkVersionForManifestFixer(ctx android.ModuleContext, sdkContext android.SdkContext) string {
+	targetSdkVersionSpec := sdkContext.TargetSdkVersion(ctx)
+	if ctx.Config().UnbundledBuildApps() && targetSdkVersionSpec.ApiLevel.IsPreview() {
+		return strconv.Itoa(android.FutureApiLevel.FinalOrFutureInt())
+	}
+	targetSdkVersion, err := targetSdkVersionSpec.EffectiveVersionString(ctx)
+	if err != nil {
+		ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
+	}
+	return targetSdkVersion
+}
 
+type ManifestFixerParams struct {
+	SdkContext            android.SdkContext
+	ClassLoaderContexts   dexpreopt.ClassLoaderContextMap
+	IsLibrary             bool
+	UseEmbeddedNativeLibs bool
+	UsesNonSdkApis        bool
+	UseEmbeddedDex        bool
+	HasNoCode             bool
+	TestOnly              bool
+	LoggingParent         string
+}
+
+// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml
+func ManifestFixer(ctx android.ModuleContext, manifest android.Path,
+	params ManifestFixerParams) android.Path {
 	var args []string
-	if isLibrary {
+
+	if params.IsLibrary {
 		args = append(args, "--library")
-	} else {
-		minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx)
+	} else if params.SdkContext != nil {
+		minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx)
 		if err != nil {
 			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
 		}
 		if minSdkVersion.FinalOrFutureInt() >= 23 {
-			args = append(args, fmt.Sprintf("--extract-native-libs=%v", !useEmbeddedNativeLibs))
-		} else if useEmbeddedNativeLibs {
+			args = append(args, fmt.Sprintf("--extract-native-libs=%v", !params.UseEmbeddedNativeLibs))
+		} else if params.UseEmbeddedNativeLibs {
 			ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%d doesn't support it",
 				minSdkVersion)
 		}
 	}
 
-	if usesNonSdkApis {
+	if params.UsesNonSdkApis {
 		args = append(args, "--uses-non-sdk-api")
 	}
 
-	if useEmbeddedDex {
+	if params.UseEmbeddedDex {
 		args = append(args, "--use-embedded-dex")
 	}
 
-	// manifest_fixer should add only the implicit SDK libraries inferred by Soong, not those added
-	// explicitly via `uses_libs`/`optional_uses_libs`.
-	requiredUsesLibs, optionalUsesLibs := classLoaderContexts.ImplicitUsesLibs()
-	for _, usesLib := range requiredUsesLibs {
-		args = append(args, "--uses-library", usesLib)
-	}
-	for _, usesLib := range optionalUsesLibs {
-		args = append(args, "--optional-uses-library", usesLib)
+	if params.ClassLoaderContexts != nil {
+		// manifest_fixer should add only the implicit SDK libraries inferred by Soong, not those added
+		// explicitly via `uses_libs`/`optional_uses_libs`.
+		requiredUsesLibs, optionalUsesLibs := params.ClassLoaderContexts.ImplicitUsesLibs()
+
+		for _, usesLib := range requiredUsesLibs {
+			args = append(args, "--uses-library", usesLib)
+		}
+		for _, usesLib := range optionalUsesLibs {
+			args = append(args, "--optional-uses-library", usesLib)
+		}
 	}
 
-	if hasNoCode {
+	if params.HasNoCode {
 		args = append(args, "--has-no-code")
 	}
 
-	if loggingParent != "" {
-		args = append(args, "--logging-parent", loggingParent)
-	}
-	var deps android.Paths
-	targetSdkVersion, err := sdkContext.TargetSdkVersion(ctx).EffectiveVersionString(ctx)
-	if err != nil {
-		ctx.ModuleErrorf("invalid targetSdkVersion: %s", err)
-	}
-	if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
-		targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
-		deps = append(deps, ApiFingerprintPath(ctx))
+	if params.TestOnly {
+		args = append(args, "--test-only")
 	}
 
-	minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
-	if err != nil {
-		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+	if params.LoggingParent != "" {
+		args = append(args, "--logging-parent", params.LoggingParent)
 	}
-	if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
-		minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
-		deps = append(deps, ApiFingerprintPath(ctx))
+	var deps android.Paths
+	var argsMapper = make(map[string]string)
+
+	if params.SdkContext != nil {
+		targetSdkVersion := targetSdkVersionForManifestFixer(ctx, params.SdkContext)
+		args = append(args, "--targetSdkVersion ", targetSdkVersion)
+
+		if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
+			targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
+			deps = append(deps, ApiFingerprintPath(ctx))
+		}
+
+		minSdkVersion, err := params.SdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx)
+		if err != nil {
+			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+		}
+
+		if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" {
+			minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String())
+			deps = append(deps, ApiFingerprintPath(ctx))
+		}
+
+		if err != nil {
+			ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
+		}
+		args = append(args, "--minSdkVersion ", minSdkVersion)
+		args = append(args, "--raise-min-sdk-version")
 	}
 
 	fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml")
-	if err != nil {
-		ctx.ModuleErrorf("invalid minSdkVersion: %s", err)
-	}
+	argsMapper["args"] = strings.Join(args, " ")
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        manifestFixerRule,
 		Description: "fix manifest",
 		Input:       manifest,
 		Implicits:   deps,
 		Output:      fixedManifest,
-		Args: map[string]string{
-			"minSdkVersion":    minSdkVersion,
-			"targetSdkVersion": targetSdkVersion,
-			"args":             strings.Join(args, " "),
-		},
+		Args:        argsMapper,
 	})
 
 	return fixedManifest.WithoutRel()
diff --git a/java/android_resources.go b/java/android_resources.go
index 6864ebb..8c5908f 100644
--- a/java/android_resources.go
+++ b/java/android_resources.go
@@ -43,7 +43,7 @@
 
 // androidResourceGlob returns the list of files in the given directory, using the standard
 // exclusion patterns for Android resources.
-func androidResourceGlob(ctx android.ModuleContext, dir android.Path) android.Paths {
+func androidResourceGlob(ctx android.EarlyModuleContext, dir android.Path) android.Paths {
 	return ctx.GlobFiles(filepath.Join(dir.String(), "**/*"), androidResourceIgnoreFilenames)
 }
 
diff --git a/java/androidmk.go b/java/androidmk.go
index 272a4fd..b930441 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -182,7 +182,14 @@
 }
 
 func (prebuilt *Import) AndroidMkEntries() []android.AndroidMkEntries {
-	if prebuilt.hideApexVariantFromMake || !prebuilt.ContainingSdk().Unversioned() {
+	if prebuilt.hideApexVariantFromMake {
+		// For a 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 := prebuilt.dexpreopter.AndroidMkEntriesForApex()
+		return append(dexpreoptEntries, android.AndroidMkEntries{Disabled: true})
+	}
+	if !prebuilt.ContainingSdk().Unversioned() {
 		return []android.AndroidMkEntries{android.AndroidMkEntries{
 			Disabled: true,
 		}}
@@ -426,8 +433,10 @@
 	if len(a.appProperties.Overrides) > 0 {
 		overridden = append(overridden, a.appProperties.Overrides...)
 	}
-	if a.Name() != a.installApkName {
-		overridden = append(overridden, a.Name())
+	// When APK name is overridden via PRODUCT_PACKAGE_NAME_OVERRIDES
+	// ensure that the original name is overridden.
+	if a.Stem() != a.installApkName {
+		overridden = append(overridden, a.Stem())
 	}
 	return overridden
 }
@@ -685,7 +694,7 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_CERTIFICATE", r.certificate.AndroidMkString())
-				entries.SetPath("LOCAL_MODULE_PATH", r.installDir.ToMakePath())
+				entries.SetPath("LOCAL_MODULE_PATH", r.installDir)
 				entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", r.properties.Overrides...)
 			},
 		},
diff --git a/java/app.go b/java/app.go
index c08ec06..e4432ff 100755
--- a/java/app.go
+++ b/java/app.go
@@ -43,8 +43,6 @@
 	ctx.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
 	ctx.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
 	ctx.RegisterModuleType("override_android_test", OverrideAndroidTestModuleFactory)
-
-	android.RegisterBp2BuildMutator("android_app_certificate", AndroidAppCertificateBp2Build)
 }
 
 // AndroidManifest.xml merging
@@ -139,6 +137,7 @@
 }
 
 type AndroidApp struct {
+	android.BazelModuleBase
 	Library
 	aapt
 	android.OverridableModuleBase
@@ -291,7 +290,7 @@
 
 		if minSdkVersion, err := a.MinSdkVersion(ctx).EffectiveVersion(ctx); err == nil {
 			a.checkJniLibsSdkVersion(ctx, minSdkVersion)
-			android.CheckMinSdkVersion(a, ctx, minSdkVersion)
+			android.CheckMinSdkVersion(ctx, minSdkVersion, a.WalkPayloadDeps)
 		} else {
 			ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
 		}
@@ -426,7 +425,8 @@
 
 	a.aapt.splitNames = a.appProperties.Package_splits
 	a.aapt.LoggingParent = String(a.overridableAppProperties.Logging_parent)
-	a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts, aaptLinkFlags...)
+	a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts,
+		a.usesLibraryProperties.Exclude_uses_libs, aaptLinkFlags...)
 
 	// apps manifests are handled by aapt, don't let Module see them
 	a.properties.Manifest = nil
@@ -486,7 +486,7 @@
 		a.jniLibs = jniLibs
 		if a.shouldEmbedJnis(ctx) {
 			jniJarFile = android.PathForModuleOut(ctx, "jnilibs.zip")
-			a.installPathForJNISymbols = a.installPath(ctx).ToMakePath()
+			a.installPathForJNISymbols = a.installPath(ctx)
 			TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.useEmbeddedNativeLibs(ctx))
 			for _, jni := range jniLibs {
 				if jni.coverageFile.Valid() {
@@ -622,7 +622,7 @@
 	a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex)
 
 	// Check if the install APK name needs to be overridden.
-	a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name())
+	a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Stem())
 
 	if ctx.ModuleName() == "framework-res" {
 		// framework-res.apk is installed as system/framework/framework-res.apk
@@ -943,6 +943,7 @@
 	android.InitDefaultableModule(module)
 	android.InitOverridableModule(module, &module.appProperties.Overrides)
 	android.InitApexModule(module)
+	android.InitBazelModule(module)
 
 	return module
 }
@@ -1006,6 +1007,7 @@
 	command := rule.Command().BuiltTool("test_config_fixer").Input(testConfig).Output(fixedConfig)
 	fixNeeded := false
 
+	// Auto-generated test config uses `ModuleName` as the APK name. So fix it if it is not the case.
 	if ctx.ModuleName() != a.installApkName {
 		fixNeeded = true
 		command.FlagWithArg("--test-file-name ", a.installApkName+".apk")
@@ -1162,7 +1164,10 @@
 // some of its properties.
 func OverrideAndroidAppModuleFactory() android.Module {
 	m := &OverrideAndroidApp{}
-	m.AddProperties(&overridableAppProperties{})
+	m.AddProperties(
+		&OverridableDeviceProperties{},
+		&overridableAppProperties{},
+	)
 
 	android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
 	android.InitOverrideModule(m)
@@ -1207,6 +1212,23 @@
 	// libraries, because SDK ones are automatically picked up by Soong. The <uses-library> name
 	// normally is the same as the module name, but there are exceptions.
 	Provides_uses_lib *string
+
+	// A list of shared library names to exclude from the classpath of the APK. Adding a library here
+	// will prevent it from being used when precompiling the APK and prevent it from being implicitly
+	// added to the APK's manifest's <uses-library> elements.
+	//
+	// Care must be taken when using this as it could result in runtime errors if the APK actually
+	// uses classes provided by the library and which are not provided in any other way.
+	//
+	// This is primarily intended for use by various CTS tests that check the runtime handling of the
+	// android.test.base shared library (and related libraries) but which depend on some common
+	// libraries that depend on the android.test.base library. Without this those tests will end up
+	// with a <uses-library android:name="android.test.base"/> in their manifest which would either
+	// render the tests worthless (as they would be testing the wrong behavior), or would break the
+	// test altogether by providing access to classes that the tests were not expecting. Those tests
+	// provide the android.test.base statically and use jarjar to rename them so they do not collide
+	// with the classes provided by the android.test.base library.
+	Exclude_uses_libs []string
 }
 
 // usesLibrary provides properties and helper functions for AndroidApp and AndroidAppImport to verify that the
@@ -1405,23 +1427,11 @@
 	Certificate string
 }
 
-func AndroidAppCertificateBp2Build(ctx android.TopDownMutatorContext) {
-	module, ok := ctx.Module().(*AndroidAppCertificate)
-	if !ok {
-		// Not an Android app certificate
-		return
-	}
-	if !module.ConvertWithBp2build(ctx) {
-		return
-	}
-	if ctx.ModuleType() != "android_app_certificate" {
-		return
-	}
-
-	androidAppCertificateBp2BuildInternal(ctx, module)
+func (m *AndroidAppCertificate) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	androidAppCertificateBp2Build(ctx, m)
 }
 
-func androidAppCertificateBp2BuildInternal(ctx android.TopDownMutatorContext, module *AndroidAppCertificate) {
+func androidAppCertificateBp2Build(ctx android.TopDownMutatorContext, module *AndroidAppCertificate) {
 	var certificate string
 	if module.properties.Certificate != nil {
 		certificate = *module.properties.Certificate
@@ -1438,3 +1448,38 @@
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
 }
+
+type bazelAndroidAppAttributes struct {
+	*javaLibraryAttributes
+	Manifest       bazel.Label
+	Custom_package *string
+	Resource_files bazel.LabelListAttribute
+}
+
+// ConvertWithBp2build is used to convert android_app to Bazel.
+func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	libAttrs := a.convertLibraryAttrsBp2Build(ctx)
+
+	manifest := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
+
+	resourceFiles := bazel.LabelList{
+		Includes: []bazel.Label{},
+	}
+	for _, dir := range android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res") {
+		files := android.RootToModuleRelativePaths(ctx, androidResourceGlob(ctx, dir))
+		resourceFiles.Includes = append(resourceFiles.Includes, files...)
+	}
+
+	attrs := &bazelAndroidAppAttributes{
+		libAttrs,
+		android.BazelLabelForModuleSrcSingle(ctx, manifest),
+		// TODO(b/209576404): handle package name override by product variable PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
+		a.overridableAppProperties.Package_name,
+		bazel.MakeLabelListAttribute(resourceFiles),
+	}
+	props := bazel.BazelTargetModuleProperties{Rule_class: "android_binary",
+		Bzl_load_location: "@rules_android//rules:rules.bzl"}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: a.Name()}, attrs)
+
+}
diff --git a/java/app_import.go b/java/app_import.go
index 4d1969e..3e5f972 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -108,8 +108,6 @@
 	return true
 }
 
-func (a *AndroidAppImport) InstallBypassMake() bool { return true }
-
 // Updates properties with variant-specific values.
 func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) {
 	config := ctx.Config()
diff --git a/java/app_test.go b/java/app_test.go
index 0aae928..16bbec1 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1707,7 +1707,7 @@
 			},
 		},
 		{
-			name: "overridden",
+			name: "overridden via PRODUCT_PACKAGE_NAME_OVERRIDES",
 			bp: `
 				android_app {
 					name: "foo",
@@ -1722,6 +1722,22 @@
 				"out/soong/target/product/test_device/system/app/bar/bar.apk",
 			},
 		},
+		{
+			name: "overridden via stem",
+			bp: `
+				android_app {
+					name: "foo",
+					srcs: ["a.java"],
+					sdk_version: "current",
+					stem: "bar",
+				}
+			`,
+			packageNameOverride: "",
+			expected: []string{
+				"out/soong/.intermediates/foo/android_common/bar.apk",
+				"out/soong/target/product/test_device/system/app/bar/bar.apk",
+			},
+		},
 	}
 
 	for _, test := range testCases {
@@ -1965,6 +1981,80 @@
 	}
 }
 
+func TestOverrideAndroidAppStem(t *testing.T) {
+	ctx, _ := testJava(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+		}
+		override_android_app {
+			name: "bar",
+			base: "foo",
+		}
+		override_android_app {
+			name: "baz",
+			base: "foo",
+			stem: "baz_stem",
+		}
+		android_app {
+			name: "foo2",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			stem: "foo2_stem",
+		}
+		override_android_app {
+			name: "bar2",
+			base: "foo2",
+		}
+		override_android_app {
+			name: "baz2",
+			base: "foo2",
+			stem: "baz2_stem",
+		}
+	`)
+	for _, expected := range []struct {
+		moduleName  string
+		variantName string
+		apkPath     string
+	}{
+		{
+			moduleName:  "foo",
+			variantName: "android_common",
+			apkPath:     "out/soong/target/product/test_device/system/app/foo/foo.apk",
+		},
+		{
+			moduleName:  "foo",
+			variantName: "android_common_bar",
+			apkPath:     "out/soong/target/product/test_device/system/app/bar/bar.apk",
+		},
+		{
+			moduleName:  "foo",
+			variantName: "android_common_baz",
+			apkPath:     "out/soong/target/product/test_device/system/app/baz_stem/baz_stem.apk",
+		},
+		{
+			moduleName:  "foo2",
+			variantName: "android_common",
+			apkPath:     "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
+		},
+		{
+			moduleName:  "foo2",
+			variantName: "android_common_bar2",
+			// Note that this may cause the duplicate output error.
+			apkPath: "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
+		},
+		{
+			moduleName:  "foo2",
+			variantName: "android_common_baz2",
+			apkPath:     "out/soong/target/product/test_device/system/app/baz2_stem/baz2_stem.apk",
+		},
+	} {
+		variant := ctx.ModuleForTests(expected.moduleName, expected.variantName)
+		variant.Output(expected.apkPath)
+	}
+}
+
 func TestOverrideAndroidAppDependency(t *testing.T) {
 	ctx, _ := testJava(t, `
 		android_app {
@@ -2168,10 +2258,33 @@
 				t.Errorf("test_config_fixer was not expected to run, but did: %q", params.RuleParams.Command)
 			}
 		}
-
 	}
 }
 
+func TestInstrumentationTargetPrebuilt(t *testing.T) {
+	bp := `
+		android_app_import {
+			name: "foo",
+			apk: "foo.apk",
+			presigned: true,
+		}
+
+		android_test {
+			name: "bar",
+			srcs: ["a.java"],
+			instrumentation_for: "foo",
+			sdk_version: "current",
+		}
+		`
+
+	android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).ExtendWithErrorHandler(
+		android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			"instrumentation_for: dependency \"foo\" of type \"android_app_import\" does not provide JavaInfo so is unsuitable for use with this property")).
+		RunTestWithBp(t, bp)
+}
+
 func TestStl(t *testing.T) {
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
@@ -2399,7 +2512,7 @@
 		`--uses-library qux ` +
 		`--uses-library quuz ` +
 		`--uses-library runtime-library`
-	android.AssertStringEquals(t, "manifest_fixer args", expectManifestFixerArgs, actualManifestFixerArgs)
+	android.AssertStringDoesContain(t, "manifest_fixer args", actualManifestFixerArgs, expectManifestFixerArgs)
 
 	// Test that all libraries are verified (library order matters).
 	verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command
@@ -2873,3 +2986,76 @@
 		t.Errorf("App does not use library proguard config")
 	}
 }
+
+func TestTargetSdkVersionManifestFixer(t *testing.T) {
+	platform_sdk_codename := "Tiramisu"
+	testCases := []struct {
+		name                     string
+		targetSdkVersionInBp     string
+		targetSdkVersionExpected string
+		unbundledBuild           bool
+	}{
+		{
+			name:                     "Non-Unbundled build: Android.bp has targetSdkVersion",
+			targetSdkVersionInBp:     "30",
+			targetSdkVersionExpected: "30",
+			unbundledBuild:           false,
+		},
+		{
+			name:                     "Unbundled build: Android.bp has targetSdkVersion",
+			targetSdkVersionInBp:     "30",
+			targetSdkVersionExpected: "30",
+			unbundledBuild:           true,
+		},
+		{
+			name:                     "Non-Unbundled build: Android.bp has targetSdkVersion equal to platform_sdk_codename",
+			targetSdkVersionInBp:     platform_sdk_codename,
+			targetSdkVersionExpected: platform_sdk_codename,
+			unbundledBuild:           false,
+		},
+		{
+			name:                     "Unbundled build: Android.bp has targetSdkVersion equal to platform_sdk_codename",
+			targetSdkVersionInBp:     platform_sdk_codename,
+			targetSdkVersionExpected: "10000",
+			unbundledBuild:           true,
+		},
+
+		{
+			name:                     "Non-Unbundled build: Android.bp has no targetSdkVersion",
+			targetSdkVersionExpected: platform_sdk_codename,
+			unbundledBuild:           false,
+		},
+		{
+			name:                     "Unbundled build: Android.bp has no targetSdkVersion",
+			targetSdkVersionExpected: "10000",
+			unbundledBuild:           true,
+		},
+	}
+	for _, testCase := range testCases {
+		bp := fmt.Sprintf(`
+			android_app {
+				name: "foo",
+				sdk_version: "current",
+				target_sdk_version: "%v",
+			}
+			`, testCase.targetSdkVersionInBp)
+		fixture := android.GroupFixturePreparers(
+			prepareForJavaTest,
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				// explicitly set platform_sdk_codename to make the test deterministic
+				variables.Platform_sdk_codename = &platform_sdk_codename
+				variables.Platform_version_active_codenames = []string{platform_sdk_codename}
+				// create a non-empty list if unbundledBuild==true
+				if testCase.unbundledBuild {
+					variables.Unbundled_build_apps = []string{"apex_a", "apex_b"}
+				}
+			}),
+		)
+
+		result := fixture.RunTestWithBp(t, bp)
+		foo := result.ModuleForTests("foo", "android_common")
+
+		manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+		android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
+	}
+}
diff --git a/java/base.go b/java/base.go
index 2f90db2..42d7733 100644
--- a/java/base.go
+++ b/java/base.go
@@ -245,9 +245,6 @@
 	// otherwise provides defaults libraries to add to the bootclasspath.
 	System_modules *string
 
-	// set the name of the output
-	Stem *string
-
 	IsSDKLibrary bool `blueprint:"mutated"`
 
 	// If true, generate the signature file of APK Signing Scheme V4, along side the signed APK file.
@@ -259,6 +256,15 @@
 	SyspropPublicStub string `blueprint:"mutated"`
 }
 
+// Device properties that can be overridden by overriding module (e.g. override_android_app)
+type OverridableDeviceProperties struct {
+	// set the name of the output. If not set, `name` is used.
+	// To override a module with this property set, overriding module might need to set this as well.
+	// Otherwise, both the overridden and the overriding modules will have the same output name, which
+	// can cause the duplicate output error.
+	Stem *string
+}
+
 // Functionality common to Module and Import
 //
 // It is embedded in Module so its functionality can be used by methods in Module
@@ -372,6 +378,7 @@
 	android.DefaultableModuleBase
 	android.ApexModuleBase
 	android.SdkBase
+	android.BazelModuleBase
 
 	// Functionality common to Module and Import.
 	embeddableInModuleAndImport
@@ -380,6 +387,8 @@
 	protoProperties  android.ProtoProperties
 	deviceProperties DeviceProperties
 
+	overridableDeviceProperties OverridableDeviceProperties
+
 	// jar file containing header classes including static library dependencies, suitable for
 	// inserting into the bootclasspath/classpath of another compile
 	headerJarFile android.Path
@@ -535,6 +544,7 @@
 	j.addHostProperties()
 	j.AddProperties(
 		&j.deviceProperties,
+		&j.overridableDeviceProperties,
 		&j.dexer.dexProperties,
 		&j.dexpreoptProperties,
 		&j.linter.properties,
@@ -958,6 +968,7 @@
 	if flags.javaVersion.usesJavaModules() {
 		j.properties.Srcs = append(j.properties.Srcs, j.properties.Openjdk9.Srcs...)
 	}
+
 	srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
 	if hasSrcExt(srcFiles.Strings(), ".proto") {
 		flags = protoFlags(ctx, &j.properties, &j.protoProperties, flags)
@@ -1643,8 +1654,7 @@
 }
 
 // Implements android.ApexModule
-func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
-	sdkVersion android.ApiLevel) error {
+func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
 	sdkSpec := j.MinSdkVersion(ctx)
 	if !sdkSpec.Specified() {
 		return fmt.Errorf("min_sdk_version is not specified")
@@ -1659,7 +1669,7 @@
 }
 
 func (j *Module) Stem() string {
-	return proptools.StringDefault(j.deviceProperties.Stem, j.Name())
+	return proptools.StringDefault(j.overridableDeviceProperties.Stem, j.Name())
 }
 
 func (j *Module) JacocoReportClassesFile() android.Path {
@@ -1924,6 +1934,9 @@
 				sm := module.(SystemModulesProvider)
 				outputDir, outputDeps := sm.OutputDirAndDeps()
 				deps.systemModules = &systemModules{outputDir, outputDeps}
+
+			case instrumentationForTag:
+				ctx.PropertyErrorf("instrumentation_for", "dependency %q of type %q does not provide JavaInfo so is unsuitable for use with this property", ctx.OtherModuleName(module), ctx.OtherModuleType(module))
 			}
 		}
 
@@ -1953,3 +1966,16 @@
 }
 
 var _ ModuleWithStem = (*Module)(nil)
+
+func (j *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	switch ctx.ModuleType() {
+	case "java_library", "java_library_host", "java_library_static":
+		if lib, ok := ctx.Module().(*Library); ok {
+			javaLibraryBp2Build(ctx, lib)
+		}
+	case "java_binary_host":
+		if binary, ok := ctx.Module().(*Binary); ok {
+			javaBinaryHostBp2Build(ctx, binary)
+		}
+	}
+}
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index bfa6838..5fe409e 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -219,6 +219,11 @@
 
 	// Collect the module directory for IDE info in java/jdeps.go.
 	modulePaths []string
+
+	// Installs for on-device boot image files. This list has entries only if the installs should be
+	// handled by Make (e.g., the boot image should be installed on the system partition, rather than
+	// in the APEX).
+	bootImageDeviceInstalls []dexpreopterInstall
 }
 
 // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
@@ -387,9 +392,19 @@
 	// Map from arch type to the boot image files.
 	bootImageFilesByArch bootImageFilesByArch
 
+	// True if the boot image should be installed in the APEX.
+	shouldInstallBootImageInApex bool
+
 	// Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the
 	// hidden API encoded dex jar path.
 	contentModuleDexJarPaths bootDexJarByModule
+
+	// Path to the image profile file on host (or empty, if profile is not generated).
+	profilePathOnHost android.Path
+
+	// Install path of the boot image profile if it needs to be installed in the APEX, or empty if not
+	// needed.
+	profileInstallPathInApex string
 }
 
 func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarList {
@@ -403,6 +418,11 @@
 	return i.bootImageFilesByArch
 }
 
+// Return true if the boot image should be installed in the APEX.
+func (i *BootclasspathFragmentApexContentInfo) ShouldInstallBootImageInApex() bool {
+	return i.shouldInstallBootImageInApex
+}
+
 // DexBootJarPathForContentModule returns the path to the dex boot jar for specified module.
 //
 // The dex boot jar is one which has had hidden API encoding performed on it.
@@ -418,6 +438,14 @@
 	}
 }
 
+func (i BootclasspathFragmentApexContentInfo) ProfilePathOnHost() android.Path {
+	return i.profilePathOnHost
+}
+
+func (i BootclasspathFragmentApexContentInfo) ProfileInstallPathInApex() string {
+	return i.profileInstallPathInApex
+}
+
 func (b *BootclasspathFragmentModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
 	tag := ctx.OtherModuleDependencyTag(dep)
 	if IsBootclasspathFragmentContentDepTag(tag) {
@@ -535,6 +563,24 @@
 				// Copy the dex jars of this fragment's content modules to their predefined locations.
 				copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
 			}
+
+			for _, variant := range imageConfig.apexVariants() {
+				arch := variant.target.Arch.ArchType.String()
+				for _, install := range variant.deviceInstalls {
+					// 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)
+					installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
+
+					b.bootImageDeviceInstalls = append(b.bootImageDeviceInstalls, dexpreopterInstall{
+						name:                arch + "-" + installBase,
+						moduleName:          b.Name(),
+						outputPathOnHost:    install.From,
+						installDirOnDevice:  installPath,
+						installFileOnDevice: installBase,
+					})
+				}
+			}
 		}
 
 		// A prebuilt fragment cannot contribute to an apex.
@@ -579,6 +625,13 @@
 
 	if imageConfig != nil {
 		info.modules = imageConfig.modules
+		global := dexpreopt.GetGlobalConfig(ctx)
+		if !global.DisableGenerateProfile {
+			info.profilePathOnHost = imageConfig.profilePathOnHost
+			info.profileInstallPathInApex = imageConfig.profileInstallPathInApex
+		}
+
+		info.shouldInstallBootImageInApex = imageConfig.shouldInstallInApex()
 	}
 
 	info.bootImageFilesByArch = bootImageFilesByArch
@@ -616,6 +669,8 @@
 	// This is an exception to support end-to-end test for SdkExtensions, until such support exists.
 	if android.InList("test_framework-sdkextensions", possibleUpdatableModules) {
 		jars = jars.Append("com.android.sdkext", "test_framework-sdkextensions")
+	} else if android.InList("AddNewActivity", possibleUpdatableModules) {
+		jars = jars.Append("test_com.android.cts.frameworkresapkplits", "AddNewActivity")
 	} else if android.InList("test_framework-apexd", possibleUpdatableModules) {
 		jars = jars.Append("com.android.apex.test_package", "test_framework-apexd")
 	} else if global.ApexBootJars.Len() != 0 && !android.IsModuleInVersionedSdk(ctx.Module()) {
@@ -793,6 +848,23 @@
 	return androidBootImageFilesByArch
 }
 
+func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntries {
+	var entriesList []android.AndroidMkEntries
+	for _, install := range b.bootImageDeviceInstalls {
+		entriesList = append(entriesList, install.ToMakeEntries())
+	}
+	return entriesList
+}
+
+// Returns the names of all Make modules that handle the installation of the boot image.
+func (b *BootclasspathFragmentModule) BootImageDeviceInstallMakeModules() []string {
+	var makeModules []string
+	for _, install := range b.bootImageDeviceInstalls {
+		makeModules = append(makeModules, install.FullModuleName())
+	}
+	return makeModules
+}
+
 // Collect information for opening IDE project files in java/jdeps.go.
 func (b *BootclasspathFragmentModule) IDEInfo(dpInfo *android.IdeInfo) {
 	dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents...)
@@ -859,13 +931,13 @@
 	All_flags_path android.OptionalPath `supported_build_releases:"S"`
 
 	// The path to the generated signature-patterns.csv file.
-	Signature_patterns_path android.OptionalPath `supported_build_releases:"T+"`
+	Signature_patterns_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
 
 	// The path to the generated filtered-stub-flags.csv file.
-	Filtered_stub_flags_path android.OptionalPath `supported_build_releases:"T+"`
+	Filtered_stub_flags_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
 
 	// The path to the generated filtered-flags.csv file.
-	Filtered_flags_path android.OptionalPath `supported_build_releases:"T+"`
+	Filtered_flags_path android.OptionalPath `supported_build_releases:"Tiramisu+"`
 }
 
 func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
@@ -1001,7 +1073,7 @@
 // At the moment this is basically just a bootclasspath_fragment module that can be used as a
 // prebuilt. Eventually as more functionality is migrated into the bootclasspath_fragment module
 // type from the various singletons then this will diverge.
-type prebuiltBootclasspathFragmentModule struct {
+type PrebuiltBootclasspathFragmentModule struct {
 	BootclasspathFragmentModule
 	prebuilt android.Prebuilt
 
@@ -1009,16 +1081,16 @@
 	prebuiltProperties prebuiltBootclasspathFragmentProperties
 }
 
-func (module *prebuiltBootclasspathFragmentModule) Prebuilt() *android.Prebuilt {
+func (module *PrebuiltBootclasspathFragmentModule) Prebuilt() *android.Prebuilt {
 	return &module.prebuilt
 }
 
-func (module *prebuiltBootclasspathFragmentModule) Name() string {
+func (module *PrebuiltBootclasspathFragmentModule) Name() string {
 	return module.prebuilt.Name(module.ModuleBase.Name())
 }
 
 // produceHiddenAPIOutput returns a path to the prebuilt all-flags.csv or nil if none is specified.
-func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
+func (module *PrebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
 	pathForOptionalSrc := func(src *string, defaultPath android.Path) android.Path {
 		if src == nil {
 			return defaultPath
@@ -1059,7 +1131,7 @@
 }
 
 // produceBootImageFiles extracts the boot image files from the APEX if available.
-func (module *prebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
+func (module *PrebuiltBootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageFilesByArch {
 	if !shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
 		return nil
 	}
@@ -1069,37 +1141,53 @@
 		return nil // An error has been reported by FindDeapexerProviderForModule.
 	}
 
-	files := bootImageFilesByArch{}
-	for _, variant := range imageConfig.apexVariants() {
-		arch := variant.target.Arch.ArchType
-		for _, toPath := range variant.imagesDeps {
-			apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base())
-			// Get the path to the file that the deapexer extracted from the prebuilt apex file.
-			fromPath := di.PrebuiltExportPath(apexRelativePath)
-
-			// Return the toPath as the calling code expects the paths in the returned map to be the
-			// paths predefined in the bootImageConfig.
-			files[arch] = append(files[arch], toPath)
-
-			// Copy the file to the predefined location.
-			ctx.Build(pctx, android.BuildParams{
-				Rule:   android.Cp,
-				Input:  fromPath,
-				Output: toPath,
-			})
-		}
+	profile := (android.WritablePath)(nil)
+	if imageConfig.profileInstallPathInApex != "" {
+		profile = di.PrebuiltExportPath(imageConfig.profileInstallPathInApex)
 	}
 
-	// Build the boot image files for the host variants. These are built from the dex files provided
-	// by the contents of this module as prebuilt versions of the host boot image files are not
-	// available, i.e. there is no host specific prebuilt apex containing them. This has to be built
-	// without a profile as the prebuilt modules do not provide a profile.
-	buildBootImageVariantsForBuildOs(ctx, imageConfig, nil)
+	// Build the boot image files for the host variants. These are always built from the dex files
+	// provided by the contents of this module as prebuilt versions of the host boot image files are
+	// not available, i.e. there is no host specific prebuilt apex containing them. This has to be
+	// built without a profile as the prebuilt modules do not provide a profile.
+	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
 
-	return files
+	if imageConfig.shouldInstallInApex() {
+		// If the boot image files for the android variants are in the prebuilt apex, we must use those
+		// rather than building new ones because those boot image files are going to be used on device.
+		files := bootImageFilesByArch{}
+		for _, variant := range imageConfig.apexVariants() {
+			arch := variant.target.Arch.ArchType
+			for _, toPath := range variant.imagesDeps {
+				apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base())
+				// Get the path to the file that the deapexer extracted from the prebuilt apex file.
+				fromPath := di.PrebuiltExportPath(apexRelativePath)
+
+				// Return the toPath as the calling code expects the paths in the returned map to be the
+				// paths predefined in the bootImageConfig.
+				files[arch] = append(files[arch], toPath)
+
+				// Copy the file to the predefined location.
+				ctx.Build(pctx, android.BuildParams{
+					Rule:   android.Cp,
+					Input:  fromPath,
+					Output: toPath,
+				})
+			}
+		}
+		return files
+	} else {
+		if profile == nil {
+			ctx.ModuleErrorf("Unable to produce boot image files: neither boot image files nor profiles exists in the prebuilt apex")
+			return nil
+		}
+		// Build boot image files for the android variants from the dex files provided by the contents
+		// of this module.
+		return buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
+	}
 }
 
-var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil)
+var _ commonBootclasspathFragment = (*PrebuiltBootclasspathFragmentModule)(nil)
 
 // createBootImageTag creates the tag to uniquely identify the boot image file among all of the
 // files that a module requires from the prebuilt .apex file.
@@ -1113,16 +1201,22 @@
 //
 // If there is no image config associated with this fragment then it returns nil. Otherwise, it
 // returns the files that are listed in the image config.
-func (module *prebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
+func (module *PrebuiltBootclasspathFragmentModule) RequiredFilesFromPrebuiltApex(ctx android.BaseModuleContext) []string {
 	imageConfig := module.getImageConfig(ctx)
 	if imageConfig != nil {
-		// Add the boot image files, e.g. .art, .oat and .vdex files.
 		files := []string{}
-		for _, variant := range imageConfig.apexVariants() {
-			arch := variant.target.Arch.ArchType
-			for _, path := range variant.imagesDeps.Paths() {
-				base := path.Base()
-				files = append(files, apexRootRelativePathToBootImageFile(arch, base))
+		if imageConfig.profileInstallPathInApex != "" {
+			// Add the boot image profile.
+			files = append(files, imageConfig.profileInstallPathInApex)
+		}
+		if imageConfig.shouldInstallInApex() {
+			// Add the boot image files, e.g. .art, .oat and .vdex files.
+			for _, variant := range imageConfig.apexVariants() {
+				arch := variant.target.Arch.ArchType
+				for _, path := range variant.imagesDeps.Paths() {
+					base := path.Base()
+					files = append(files, apexRootRelativePathToBootImageFile(arch, base))
+				}
 			}
 		}
 		return files
@@ -1134,10 +1228,10 @@
 	return filepath.Join("javalib", arch.String(), base)
 }
 
-var _ android.RequiredFilesFromPrebuiltApex = (*prebuiltBootclasspathFragmentModule)(nil)
+var _ android.RequiredFilesFromPrebuiltApex = (*PrebuiltBootclasspathFragmentModule)(nil)
 
 func prebuiltBootclasspathFragmentFactory() android.Module {
-	m := &prebuiltBootclasspathFragmentModule{}
+	m := &PrebuiltBootclasspathFragmentModule{}
 	m.AddProperties(&m.properties, &m.prebuiltProperties)
 	// This doesn't actually have any prebuilt files of its own so pass a placeholder for the srcs
 	// array.
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index e77971b..ca27528 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -204,7 +204,7 @@
 		OutputFile: android.OptionalPathForPath(c.outputFilepath),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_PATH", c.installDirPath.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", c.outputFilepath.Base())
 			},
 		},
diff --git a/java/config/config.go b/java/config/config.go
index 30c6f91..ea2f934 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -60,7 +60,7 @@
 
 const (
 	JavaVmFlags  = `-XX:OnError="cat hs_err_pid%p.log" -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads`
-	JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads`
+	JavacVmFlags = `-J-XX:OnError="cat hs_err_pid%p.log" -J-XX:CICompilerCount=6 -J-XX:+UseDynamicNumberOfGCThreads -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1`
 )
 
 func init() {
diff --git a/java/dex.go b/java/dex.go
index 8045b5c..474694a 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -72,6 +72,9 @@
 	// This defaults to reasonable value based on module and should not be set.
 	// It exists only to support ART tests.
 	Uncompress_dex *bool
+
+	// Exclude kotlinc generate files: *.kotlin_module, *.kotlin_builtins. Defaults to false.
+	Exclude_kotlinc_generated_files *bool
 }
 
 type dexer struct {
@@ -94,7 +97,7 @@
 			`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
 			`$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $tmpJar && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
-			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
+			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.D8Cmd}",
 			"${config.Zip2ZipCmd}",
@@ -116,7 +119,7 @@
 			ExecStrategy: "${config.RED8ExecStrategy}",
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
-	}, []string{"outDir", "d8Flags", "zipFlags", "tmpJar"}, nil)
+	}, []string{"outDir", "d8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, nil)
 
 var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
 	blueprint.RuleParams{
@@ -134,7 +137,7 @@
 			`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
 			`rm -rf ${outUsageDir} && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
-			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
+			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.R8Cmd}",
 			"${config.Zip2ZipCmd}",
@@ -165,7 +168,7 @@
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
 	}, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
-		"r8Flags", "zipFlags", "tmpJar"}, []string{"implicits"})
+		"r8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, []string{"implicits"})
 
 func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
 	minSdkVersion android.SdkSpec) (flags []string, deps android.Paths) {
@@ -254,6 +257,15 @@
 
 	if BoolDefault(opt.Proguard_compatibility, true) {
 		r8Flags = append(r8Flags, "--force-proguard-compatibility")
+	} else {
+		// TODO(b/213833843): Allow configuration of the prefix via a build variable.
+		var sourceFilePrefix = "go/retraceme "
+		var sourceFileTemplate = "\"" + sourceFilePrefix + "%MAP_ID\""
+		// TODO(b/200967150): Also tag the source file in compat builds.
+		if Bool(opt.Optimize) || Bool(opt.Obfuscate) {
+			r8Flags = append(r8Flags, "--map-id-template", "%MAP_HASH")
+			r8Flags = append(r8Flags, "--source-file-template", sourceFileTemplate)
+		}
 	}
 
 	// TODO(ccross): Don't shrink app instrumentation tests by default.
@@ -298,6 +310,12 @@
 
 	commonFlags, commonDeps := d.dexCommonFlags(ctx, minSdkVersion)
 
+	// Exclude kotlinc generated files when "exclude_kotlinc_generated_files" is set to true.
+	mergeZipsFlags := ""
+	if proptools.BoolDefault(d.dexProperties.Exclude_kotlinc_generated_files, false) {
+		mergeZipsFlags = "-stripFile META-INF/*.kotlin_module -stripFile **/*.kotlin_builtins"
+	}
+
 	useR8 := d.effectiveOptimizeEnabled()
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
@@ -311,14 +329,15 @@
 		r8Deps = append(r8Deps, commonDeps...)
 		rule := r8
 		args := map[string]string{
-			"r8Flags":     strings.Join(append(commonFlags, r8Flags...), " "),
-			"zipFlags":    zipFlags,
-			"outDict":     proguardDictionary.String(),
-			"outUsageDir": proguardUsageDir.String(),
-			"outUsage":    proguardUsage.String(),
-			"outUsageZip": proguardUsageZip.String(),
-			"outDir":      outDir.String(),
-			"tmpJar":      tmpJar.String(),
+			"r8Flags":        strings.Join(append(commonFlags, r8Flags...), " "),
+			"zipFlags":       zipFlags,
+			"outDict":        proguardDictionary.String(),
+			"outUsageDir":    proguardUsageDir.String(),
+			"outUsage":       proguardUsage.String(),
+			"outUsageZip":    proguardUsageZip.String(),
+			"outDir":         outDir.String(),
+			"tmpJar":         tmpJar.String(),
+			"mergeZipsFlags": mergeZipsFlags,
 		}
 		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8") {
 			rule = r8RE
@@ -347,10 +366,11 @@
 			Input:       classesJar,
 			Implicits:   d8Deps,
 			Args: map[string]string{
-				"d8Flags":  strings.Join(append(commonFlags, d8Flags...), " "),
-				"zipFlags": zipFlags,
-				"outDir":   outDir.String(),
-				"tmpJar":   tmpJar.String(),
+				"d8Flags":        strings.Join(append(commonFlags, d8Flags...), " "),
+				"zipFlags":       zipFlags,
+				"outDir":         outDir.String(),
+				"tmpJar":         tmpJar.String(),
+				"mergeZipsFlags": mergeZipsFlags,
 			},
 		})
 	}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index f3a53ee..7c5f055 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -57,6 +57,25 @@
 	return "-dexpreopt-" + install.name
 }
 
+// Returns Make entries for installing the file.
+//
+// This function uses a value receiver rather than a pointer receiver to ensure that the object is
+// safe to use in `android.AndroidMkExtraEntriesFunc`.
+func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries {
+	return 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.String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
+				entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
+			},
+		},
+	}
+}
+
 type dexpreopter struct {
 	dexpreoptProperties DexpreoptProperties
 
@@ -115,6 +134,11 @@
 	return !apexInfo.IsForPlatform()
 }
 
+func forPrebuiltApex(ctx android.BaseModuleContext) bool {
+	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	return apexInfo.ForPrebuiltApex
+}
+
 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.
@@ -134,7 +158,9 @@
 		return true
 	}
 
-	if !ctx.Module().(DexpreopterInterface).IsInstallable() {
+	// If the module is from a prebuilt APEX, it shouldn't be installable, but it can still be
+	// dexpreopted.
+	if !ctx.Module().(DexpreopterInterface).IsInstallable() && !forPrebuiltApex(ctx) {
 		return true
 	}
 
@@ -152,15 +178,16 @@
 		return true
 	}
 
+	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
 	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() {
+		if !isApexSystemServerJar || 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)) {
+		if isApexSystemServerJar {
 			return true
 		}
 	}
@@ -191,8 +218,8 @@
 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))
+	if global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx)) {
+		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, moduleName(ctx))
 		return android.PathForModuleInPartitionInstall(ctx, "", strings.TrimPrefix(dexLocation, "/"))
 	}
 	if !d.dexpreoptDisabled(ctx) && isApexVariant(ctx) &&
@@ -229,8 +256,7 @@
 		return
 	}
 
-	isSystemServerJar := global.SystemServerJars.ContainsJar(moduleName(ctx)) ||
-		global.ApexSystemServerJars.ContainsJar(moduleName(ctx))
+	isSystemServerJar := global.AllSystemServerJars(ctx).ContainsJar(moduleName(ctx))
 
 	bootImage := defaultBootImageConfig(ctx)
 	if global.UseArtImage {
@@ -336,6 +362,8 @@
 
 	dexpreoptRule.Build("dexpreopt", "dexpreopt")
 
+	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
+
 	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), "/")
@@ -343,7 +371,7 @@
 		arch := filepath.Base(installDir)
 		installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
 
-		if global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
+		if isApexSystemServerJar {
 			// 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.
@@ -362,7 +390,7 @@
 		}
 	}
 
-	if !global.ApexSystemServerJars.ContainsJar(moduleName(ctx)) {
+	if !isApexSystemServerJar {
 		d.builtInstalled = dexpreoptRule.Installs().String()
 	}
 }
@@ -374,19 +402,7 @@
 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")
-				},
-			},
-		})
+		entries = append(entries, install.ToMakeEntries())
 	}
 	return entries
 }
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index a722946..cad9c33 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -256,6 +256,10 @@
 	// Subdirectory where the image files on device are installed.
 	installDirOnDevice string
 
+	// Install path of the boot image profile if it needs to be installed in the APEX, or empty if not
+	// needed.
+	profileInstallPathInApex string
+
 	// A list of (location, jar) pairs for the Java modules in this image.
 	modules android.ConfiguredJarList
 
@@ -272,6 +276,9 @@
 	// Rules which should be used in make to install the outputs.
 	profileInstalls android.RuleBuilderInstalls
 
+	// Path to the image profile file on host (or empty, if profile is not generated).
+	profilePathOnHost android.Path
+
 	// Target-dependent fields.
 	variants []*bootImageVariant
 }
@@ -306,10 +313,13 @@
 	// This is only set for a variant of an image that extends another image.
 	primaryImagesDeps android.Paths
 
-	// Rules which should be used in make to install the outputs.
+	// Rules which should be used in make to install the outputs on host.
 	installs           android.RuleBuilderInstalls
 	vdexInstalls       android.RuleBuilderInstalls
 	unstrippedInstalls android.RuleBuilderInstalls
+
+	// Rules which should be used in make to install the outputs on device.
+	deviceInstalls android.RuleBuilderInstalls
 }
 
 // Get target-specific boot image variant for the given boot image config and target.
@@ -381,6 +391,11 @@
 	return variants
 }
 
+// Returns true if the boot image should be installed in the APEX.
+func (image *bootImageConfig) shouldInstallInApex() bool {
+	return strings.HasPrefix(image.installDirOnDevice, "apex/")
+}
+
 // Return boot image locations (as a list of symbolic paths).
 //
 // The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
@@ -499,8 +514,18 @@
 		dst := dstBootJarsByModule[name]
 
 		if src == nil {
+			// A dex boot jar should be provided by the source java module. It needs to be installable or
+			// have compile_dex=true - cf. assignments to java.Module.dexJarFile.
+			//
+			// However, the source java module may be either replaced or overridden (using prefer:true) by
+			// a prebuilt java module with the same name. In that case the dex boot jar needs to be
+			// provided by the corresponding prebuilt APEX module. That APEX is the one that refers
+			// through a exported_(boot|systemserver)classpath_fragments property to a
+			// prebuilt_(boot|systemserver)classpath_fragment module, which in turn lists the prebuilt
+			// java module in the contents property. If that chain is broken then this dependency will
+			// fail.
 			if !ctx.Config().AllowMissingDependencies() {
-				ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+				ctx.ModuleErrorf("module %s does not provide a dex boot jar (see comment next to this message in Soong for details)", name)
 			} else {
 				ctx.AddMissingDependencies([]string{name})
 			}
@@ -693,6 +718,7 @@
 
 	var vdexInstalls android.RuleBuilderInstalls
 	var unstrippedInstalls android.RuleBuilderInstalls
+	var deviceInstalls android.RuleBuilderInstalls
 
 	for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") {
 		cmd.ImplicitOutput(artOrOat)
@@ -718,12 +744,21 @@
 			android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())})
 	}
 
+	if image.installDirOnHost != image.installDirOnDevice && !image.shouldInstallInApex() && !ctx.Config().UnbundledBuild() {
+		installDirOnDevice := filepath.Join("/", image.installDirOnDevice, arch.String())
+		for _, file := range image.moduleFiles(ctx, outputDir, ".art", ".oat", ".vdex") {
+			deviceInstalls = append(deviceInstalls,
+				android.RuleBuilderInstall{file, filepath.Join(installDirOnDevice, file.Base())})
+		}
+	}
+
 	rule.Build(image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String())
 
 	// save output and installed files for makevars
 	image.installs = rule.Installs()
 	image.vdexInstalls = vdexInstalls
 	image.unstrippedInstalls = unstrippedInstalls
+	image.deviceInstalls = deviceInstalls
 }
 
 const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
@@ -769,11 +804,14 @@
 		FlagForEachArg("--dex-location=", image.getAnyAndroidVariant().dexLocationsDeps).
 		FlagWithOutput("--reference-profile-file=", profile)
 
-	rule.Install(profile, "/system/etc/boot-image.prof")
+	if image == defaultBootImageConfig(ctx) {
+		rule.Install(profile, "/system/etc/boot-image.prof")
+		image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+	}
 
 	rule.Build("bootJarsProfile", "profile boot jars")
 
-	image.profileInstalls = append(image.profileInstalls, rule.Installs()...)
+	image.profilePathOnHost = profile
 
 	return profile
 }
diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go
index 565901d..83c088c 100644
--- a/java/dexpreopt_check.go
+++ b/java/dexpreopt_check.go
@@ -59,7 +59,7 @@
 
 func getInstallPath(ctx android.ModuleContext, location string) android.InstallPath {
 	return android.PathForModuleInPartitionInstall(
-		ctx, "", strings.TrimPrefix(location, "/")).ToMakePath()
+		ctx, "", strings.TrimPrefix(location, "/"))
 }
 
 func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -72,9 +72,9 @@
 		return
 	}
 
-	systemServerJars := dexpreopt.AllSystemServerJars(ctx, global)
+	systemServerJars := global.AllSystemServerJars(ctx)
 	for _, jar := range systemServerJars.CopyOfJars() {
-		dexLocation := dexpreopt.GetSystemServerDexLocation(global, jar)
+		dexLocation := dexpreopt.GetSystemServerDexLocation(ctx, global, jar)
 		odexLocation := dexpreopt.ToOdexPath(dexLocation, targets[0].Arch.ArchType)
 		odexPath := getInstallPath(ctx, odexLocation)
 		vdexPath := getInstallPath(ctx, pathtools.ReplaceExtension(odexLocation, "vdex"))
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 415a1d4..21e1d12 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -41,37 +41,32 @@
 
 var (
 	bootImageConfigKey     = android.NewOnceKey("bootImageConfig")
+	bootImageConfigRawKey  = android.NewOnceKey("bootImageConfigRaw")
 	artBootImageName       = "art"
 	frameworkBootImageName = "boot"
 )
 
-// Construct the global boot image configs.
-func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
-	return ctx.Config().Once(bootImageConfigKey, func() interface{} {
-
+func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig {
+	return ctx.Config().Once(bootImageConfigRawKey, func() interface{} {
 		global := dexpreopt.GetGlobalConfig(ctx)
-		targets := dexpreoptTargets(ctx)
-		deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
 
 		artModules := global.ArtApexJars
 		frameworkModules := global.BootJars.RemoveList(artModules)
 
-		artDirOnHost := "apex/art_boot_images/javalib"
-		artDirOnDevice := "apex/com.android.art/javalib"
-		frameworkSubdir := "system/framework"
-
 		// ART config for the primary boot image in the ART apex.
 		// It includes the Core Libraries.
 		artCfg := bootImageConfig{
-			name:               artBootImageName,
-			stem:               "boot",
-			installDirOnHost:   artDirOnHost,
-			installDirOnDevice: artDirOnDevice,
-			modules:            artModules,
+			name:                     artBootImageName,
+			stem:                     "boot",
+			installDirOnHost:         "apex/art_boot_images/javalib",
+			installDirOnDevice:       "system/framework",
+			profileInstallPathInApex: "etc/boot-image.prof",
+			modules:                  artModules,
 		}
 
 		// Framework config for the boot image extension.
 		// It includes framework libraries and depends on the ART config.
+		frameworkSubdir := "system/framework"
 		frameworkCfg := bootImageConfig{
 			extends:            &artCfg,
 			name:               frameworkBootImageName,
@@ -81,10 +76,22 @@
 			modules:            frameworkModules,
 		}
 
-		configs := map[string]*bootImageConfig{
+		return map[string]*bootImageConfig{
 			artBootImageName:       &artCfg,
 			frameworkBootImageName: &frameworkCfg,
 		}
+	}).(map[string]*bootImageConfig)
+}
+
+// Construct the global boot image configs.
+func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
+	return ctx.Config().Once(bootImageConfigKey, func() interface{} {
+		targets := dexpreoptTargets(ctx)
+		deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
+
+		configs := genBootImageConfigRaw(ctx)
+		artCfg := configs[artBootImageName]
+		frameworkCfg := configs[frameworkBootImageName]
 
 		// common to all configs
 		for _, c := range configs {
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 7ad316f..f9dcfd6 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -334,7 +334,11 @@
 		// TODO(tnorbye): find owners to fix these warnings when annotation was enabled.
 		cmd.FlagWithArg("--hide ", "HiddenTypedefConstant").
 			FlagWithArg("--hide ", "SuperfluousPrefix").
-			FlagWithArg("--hide ", "AnnotationExtraction")
+			FlagWithArg("--hide ", "AnnotationExtraction").
+			// (b/217545629)
+			FlagWithArg("--hide ", "ChangedThrows").
+			// (b/217552813)
+			FlagWithArg("--hide ", "ChangedAbstract")
 	}
 }
 
diff --git a/java/fuzz.go b/java/fuzz.go
new file mode 100644
index 0000000..257f343
--- /dev/null
+++ b/java/fuzz.go
@@ -0,0 +1,159 @@
+// 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 java
+
+import (
+	"github.com/google/blueprint/proptools"
+	"sort"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/fuzz"
+)
+
+func init() {
+	RegisterJavaFuzzBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterJavaFuzzBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("java_fuzz_host", FuzzFactory)
+	ctx.RegisterSingletonType("java_fuzz_packaging", javaFuzzPackagingFactory)
+}
+
+type JavaFuzzLibrary struct {
+	Library
+	fuzzPackagedModule fuzz.FuzzPackagedModule
+}
+
+func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	j.Library.GenerateAndroidBuildActions(ctx)
+
+	if j.fuzzPackagedModule.FuzzProperties.Corpus != nil {
+		j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus)
+	}
+	if j.fuzzPackagedModule.FuzzProperties.Data != nil {
+		j.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Data)
+	}
+	if j.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
+		j.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *j.fuzzPackagedModule.FuzzProperties.Dictionary)
+	}
+
+	if j.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
+		configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
+		android.WriteFileRule(ctx, configPath, j.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
+		j.fuzzPackagedModule.Config = configPath
+	}
+}
+
+// java_fuzz builds and links sources into a `.jar` file for the host.
+//
+// By default, a java_fuzz produces a `.jar` file containing `.class` files.
+// This jar is not suitable for installing on a device.
+func FuzzFactory() android.Module {
+	module := &JavaFuzzLibrary{}
+
+	module.addHostProperties()
+	module.Module.properties.Installable = proptools.BoolPtr(false)
+	module.AddProperties(&module.fuzzPackagedModule.FuzzProperties)
+
+	// java_fuzz packaging rules collide when both linux_glibc and linux_bionic are enabled, disable the linux_bionic variants.
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		disableLinuxBionic := struct {
+			Target struct {
+				Linux_bionic struct {
+					Enabled *bool
+				}
+			}
+		}{}
+		disableLinuxBionic.Target.Linux_bionic.Enabled = proptools.BoolPtr(false)
+		ctx.AppendProperties(&disableLinuxBionic)
+	})
+
+	module.initModuleAndImport(module)
+	android.InitSdkAwareModule(module)
+	InitJavaModule(module, android.HostSupported)
+	return module
+}
+
+// Responsible for generating rules that package fuzz targets into
+// their architecture & target/host specific zip file.
+type javaFuzzPackager struct {
+	fuzz.FuzzPackager
+}
+
+func javaFuzzPackagingFactory() android.Singleton {
+	return &javaFuzzPackager{}
+}
+
+func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
+	// Map between each architecture + host/device combination.
+	archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
+
+	// List of individual fuzz targets.
+	s.FuzzTargets = make(map[string]bool)
+
+	ctx.VisitAllModules(func(module android.Module) {
+		// Discard non-fuzz targets.
+		javaModule, ok := module.(*JavaFuzzLibrary)
+		if !ok {
+			return
+		}
+
+		fuzzModuleValidator := fuzz.FuzzModule{
+			javaModule.ModuleBase,
+			javaModule.DefaultableModuleBase,
+			javaModule.ApexModuleBase,
+		}
+
+		if ok := fuzz.IsValid(fuzzModuleValidator); !ok || *javaModule.Module.properties.Installable {
+			return
+		}
+
+		hostOrTargetString := "target"
+		if javaModule.Host() {
+			hostOrTargetString = "host"
+		}
+		archString := javaModule.Arch().ArchType.String()
+
+		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
+		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
+
+		var files []fuzz.FileToZip
+		builder := android.NewRuleBuilder(pctx, ctx)
+
+		// Package the artifacts (data, corpus, config and dictionary into a zipfile.
+		files = s.PackageArtifacts(ctx, module, javaModule.fuzzPackagedModule, archDir, builder)
+
+		// Add .jar
+		files = append(files, fuzz.FileToZip{javaModule.outputFile, ""})
+
+		archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaModule.fuzzPackagedModule, files, builder, archDir, archString, "host", archOs, archDirs)
+		if !ok {
+			return
+		}
+
+	})
+	s.CreateFuzzPackage(ctx, archDirs, fuzz.Java, pctx)
+}
+
+func (s *javaFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
+	packages := s.Packages.Strings()
+	sort.Strings(packages)
+
+	ctx.Strict("SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
+
+	// Preallocate the slice of fuzz targets to minimize memory allocations.
+	s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_TARGETS")
+}
diff --git a/java/fuzz_test.go b/java/fuzz_test.go
new file mode 100644
index 0000000..cf063eb
--- /dev/null
+++ b/java/fuzz_test.go
@@ -0,0 +1,65 @@
+// 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 java
+
+import (
+	"android/soong/android"
+	"path/filepath"
+	"testing"
+)
+
+var prepForJavaFuzzTest = android.GroupFixturePreparers(
+	PrepareForTestWithJavaDefaultModules,
+	android.FixtureRegisterWithContext(RegisterJavaFuzzBuildComponents),
+)
+
+func TestJavaFuzz(t *testing.T) {
+	result := prepForJavaFuzzTest.RunTestWithBp(t, `
+		java_fuzz_host {
+			name: "foo",
+			srcs: ["a.java"],
+			libs: ["bar"],
+			static_libs: ["baz"],
+		}
+
+		java_library_host {
+			name: "bar",
+			srcs: ["b.java"],
+		}
+
+		java_library_host {
+			name: "baz",
+			srcs: ["c.java"],
+		}`)
+
+	osCommonTarget := result.Config.BuildOSCommonTarget.String()
+	javac := result.ModuleForTests("foo", osCommonTarget).Rule("javac")
+	combineJar := result.ModuleForTests("foo", osCommonTarget).Description("for javac")
+
+	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
+		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
+	}
+
+	baz := result.ModuleForTests("baz", osCommonTarget).Rule("javac").Output.String()
+	barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "javac", "bar.jar")
+	bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "javac", "baz.jar")
+
+	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barOut)
+	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], bazOut)
+
+	if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz {
+		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz)
+	}
+}
diff --git a/java/genrule.go b/java/genrule.go
index 16743b3..5047c41 100644
--- a/java/genrule.go
+++ b/java/genrule.go
@@ -24,8 +24,8 @@
 }
 
 func RegisterGenRuleBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("java_genrule", genRuleFactory)
-	ctx.RegisterModuleType("java_genrule_host", genRuleFactoryHost)
+	ctx.RegisterModuleType("java_genrule", GenRuleFactory)
+	ctx.RegisterModuleType("java_genrule_host", GenRuleFactoryHost)
 }
 
 // java_genrule is a genrule that can depend on other java_* objects.
@@ -33,7 +33,7 @@
 // By default a java_genrule has a single variant that will run against the device variant of its dependencies and
 // produce an output that can be used as an input to a device java rule.
 //
-// Specifying `host_supported: true` will produce two variants, one that uses device dependencie sand one that uses
+// Specifying `host_supported: true` will produce two variants, one that uses device dependencies and one that uses
 // host dependencies.  Each variant will run the command.
 //
 // Use a java_genrule instead of a genrule when it needs to depend on or be depended on by other java modules, unless
@@ -44,7 +44,7 @@
 // Use a java_genrule to package generated java resources:
 //
 //     java_genrule {
-//     name: "generated_resources",
+//         name: "generated_resources",
 //         tools: [
 //             "generator",
 //             "soong_zip",
@@ -60,11 +60,12 @@
 //         srcs: ["src/**/*.java"],
 //         static_libs: ["generated_resources"],
 //     }
-func genRuleFactory() android.Module {
+func GenRuleFactory() android.Module {
 	module := genrule.NewGenRule()
 
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+	android.InitBazelModule(module)
 
 	return module
 }
@@ -73,11 +74,12 @@
 //
 // A java_genrule_host has a single variant that will run against the host variant of its dependencies and
 // produce an output that can be used as an input to a host java rule.
-func genRuleFactoryHost() android.Module {
+func GenRuleFactoryHost() android.Module {
 	module := genrule.NewGenRule()
 
 	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+	android.InitBazelModule(module)
 
 	return module
 }
diff --git a/java/genrule_test.go b/java/genrule_test.go
new file mode 100644
index 0000000..1c294b2
--- /dev/null
+++ b/java/genrule_test.go
@@ -0,0 +1,118 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"reflect"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func testGenruleContext(config android.Config) *android.TestContext {
+	ctx := android.NewTestArchContext(config)
+	ctx.RegisterModuleType("java_genrule", GenRuleFactory)
+	ctx.Register()
+
+	return ctx
+}
+
+func TestGenruleCmd(t *testing.T) {
+	fs := map[string][]byte{
+		"tool": nil,
+		"foo":  nil,
+	}
+	bp := `
+				java_genrule {
+					name: "gen",
+					tool_files: ["tool"],
+					cmd: "$(location tool) $(in) $(out)",
+					srcs: ["foo"],
+					out: ["out"],
+				}
+			`
+	config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
+
+	ctx := testGenruleContext(config)
+
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	if errs == nil {
+		_, errs = ctx.PrepareBuildActions(config)
+	}
+	if errs != nil {
+		t.Fatal(errs)
+	}
+
+	gen := ctx.ModuleForTests("gen", "android_common").Output("out")
+	expected := []string{"foo"}
+	if !reflect.DeepEqual(expected, gen.Implicits.Strings()[:len(expected)]) {
+		t.Errorf(`want arm inputs %v, got %v`, expected, gen.Implicits.Strings())
+	}
+}
+
+func TestJarGenrules(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+		}
+
+		java_genrule {
+			name: "jargen",
+			tool_files: ["b.java"],
+			cmd: "$(location b.java) $(in) $(out)",
+			out: ["jargen.jar"],
+			srcs: [":foo"],
+		}
+
+		java_library {
+			name: "bar",
+			static_libs: ["jargen"],
+			srcs: ["c.java"],
+		}
+
+		java_library {
+			name: "baz",
+			libs: ["jargen"],
+			srcs: ["c.java"],
+		}
+	`)
+
+	foo := ctx.ModuleForTests("foo", "android_common").Output("javac/foo.jar")
+	jargen := ctx.ModuleForTests("jargen", "android_common").Output("jargen.jar")
+	bar := ctx.ModuleForTests("bar", "android_common").Output("javac/bar.jar")
+	baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar")
+	barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar")
+
+	if g, w := jargen.Implicits.Strings(), foo.Output.String(); !android.InList(w, g) {
+		t.Errorf("expected jargen inputs [%q], got %q", w, g)
+	}
+
+	if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) {
+		t.Errorf("bar classpath %v does not contain %q", bar.Args["classpath"], jargen.Output.String())
+	}
+
+	if !strings.Contains(baz.Args["classpath"], jargen.Output.String()) {
+		t.Errorf("baz classpath %v does not contain %q", baz.Args["classpath"], jargen.Output.String())
+	}
+
+	if len(barCombined.Inputs) != 2 ||
+		barCombined.Inputs[0].String() != bar.Output.String() ||
+		barCombined.Inputs[1].String() != jargen.Output.String() {
+		t.Errorf("bar combined jar inputs %v is not [%q, %q]",
+			barCombined.Inputs.Strings(), bar.Output.String(), jargen.Output.String())
+	}
+}
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 7c8be1e..3af5f1c 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -297,6 +297,7 @@
 
 type hiddenApiAnnotationsDependencyTag struct {
 	blueprint.BaseDependencyTag
+	android.LicenseAnnotationSharedDependencyTag
 }
 
 // Tag used to mark dependencies on java_library instances that contains Java source files whose
diff --git a/java/java.go b/java/java.go
index a9f3d1a..e55f045 100644
--- a/java/java.go
+++ b/java/java.go
@@ -21,6 +21,9 @@
 import (
 	"fmt"
 	"path/filepath"
+	"strings"
+
+	"android/soong/bazel"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -265,11 +268,15 @@
 	return j.kytheFiles
 }
 
-func (j *Module) InstallBypassMake() bool { return true }
-
 type dependencyTag struct {
 	blueprint.BaseDependencyTag
 	name string
+
+	// True if the dependency is relinked at runtime.
+	runtimeLinked bool
+
+	// True if the dependency is a toolchain, for example an annotation processor.
+	toolchain bool
 }
 
 // installDependencyTag is a dependency tag that is annotated to cause the installed files of the
@@ -280,6 +287,17 @@
 	name string
 }
 
+func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
+	if d.runtimeLinked {
+		return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency}
+	} else if d.toolchain {
+		return []android.LicenseAnnotation{android.LicenseAnnotationToolchain}
+	}
+	return nil
+}
+
+var _ android.LicenseAnnotationsDependencyTag = dependencyTag{}
+
 type usesLibraryDependencyTag struct {
 	dependencyTag
 
@@ -296,10 +314,13 @@
 
 func makeUsesLibraryDependencyTag(sdkVersion int, optional bool, implicit bool) usesLibraryDependencyTag {
 	return usesLibraryDependencyTag{
-		dependencyTag: dependencyTag{name: fmt.Sprintf("uses-library-%d", sdkVersion)},
-		sdkVersion:    sdkVersion,
-		optional:      optional,
-		implicit:      implicit,
+		dependencyTag: dependencyTag{
+			name:          fmt.Sprintf("uses-library-%d", sdkVersion),
+			runtimeLinked: true,
+		},
+		sdkVersion: sdkVersion,
+		optional:   optional,
+		implicit:   implicit,
 	}
 }
 
@@ -309,23 +330,24 @@
 
 var (
 	dataNativeBinsTag       = dependencyTag{name: "dataNativeBins"}
+	dataDeviceBinsTag       = dependencyTag{name: "dataDeviceBins"}
 	staticLibTag            = dependencyTag{name: "staticlib"}
-	libTag                  = dependencyTag{name: "javalib"}
-	java9LibTag             = dependencyTag{name: "java9lib"}
-	pluginTag               = dependencyTag{name: "plugin"}
-	errorpronePluginTag     = dependencyTag{name: "errorprone-plugin"}
-	exportedPluginTag       = dependencyTag{name: "exported-plugin"}
-	bootClasspathTag        = dependencyTag{name: "bootclasspath"}
-	systemModulesTag        = dependencyTag{name: "system modules"}
+	libTag                  = dependencyTag{name: "javalib", runtimeLinked: true}
+	java9LibTag             = dependencyTag{name: "java9lib", runtimeLinked: true}
+	pluginTag               = dependencyTag{name: "plugin", toolchain: true}
+	errorpronePluginTag     = dependencyTag{name: "errorprone-plugin", toolchain: true}
+	exportedPluginTag       = dependencyTag{name: "exported-plugin", toolchain: true}
+	bootClasspathTag        = dependencyTag{name: "bootclasspath", runtimeLinked: true}
+	systemModulesTag        = dependencyTag{name: "system modules", runtimeLinked: true}
 	frameworkResTag         = dependencyTag{name: "framework-res"}
-	kotlinStdlibTag         = dependencyTag{name: "kotlin-stdlib"}
-	kotlinAnnotationsTag    = dependencyTag{name: "kotlin-annotations"}
-	kotlinPluginTag         = dependencyTag{name: "kotlin-plugin"}
+	kotlinStdlibTag         = dependencyTag{name: "kotlin-stdlib", runtimeLinked: true}
+	kotlinAnnotationsTag    = dependencyTag{name: "kotlin-annotations", runtimeLinked: true}
+	kotlinPluginTag         = dependencyTag{name: "kotlin-plugin", toolchain: true}
 	proguardRaiseTag        = dependencyTag{name: "proguard-raise"}
 	certificateTag          = dependencyTag{name: "certificate"}
 	instrumentationForTag   = dependencyTag{name: "instrumentation_for"}
-	extraLintCheckTag       = dependencyTag{name: "extra-lint-check"}
-	jniLibTag               = dependencyTag{name: "jnilib"}
+	extraLintCheckTag       = dependencyTag{name: "extra-lint-check", toolchain: true}
+	jniLibTag               = dependencyTag{name: "jnilib", runtimeLinked: true}
 	syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
 	jniInstallTag           = installDependencyTag{name: "jni install"}
 	binaryInstallTag        = installDependencyTag{name: "binary install"}
@@ -435,7 +457,7 @@
 	} else if ctx.Device() {
 		return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
 	} else {
-		return JAVA_VERSION_9
+		return JAVA_VERSION_11
 	}
 }
 
@@ -735,6 +757,7 @@
 
 	android.InitApexModule(module)
 	android.InitSdkAwareModule(module)
+	android.InitBazelModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	return module
 }
@@ -757,6 +780,7 @@
 
 	android.InitApexModule(module)
 	android.InitSdkAwareModule(module)
+	android.InitBazelModule(module)
 	InitJavaModule(module, android.HostSupported)
 	return module
 }
@@ -813,6 +837,9 @@
 type hostTestProperties struct {
 	// list of native binary modules that should be installed alongside the test
 	Data_native_bins []string `android:"arch_variant"`
+
+	// list of device binary modules that should be installed alongside the test
+	Data_device_bins []string `android:"arch_variant"`
 }
 
 type testHelperLibraryProperties struct {
@@ -886,6 +913,11 @@
 		}
 	}
 
+	if len(j.testHostProperties.Data_device_bins) > 0 {
+		deviceVariations := ctx.Config().AndroidFirstDeviceTarget.Variations()
+		ctx.AddFarVariationDependencies(deviceVariations, dataDeviceBinsTag, j.testHostProperties.Data_device_bins...)
+	}
+
 	if len(j.testProperties.Jni_libs) > 0 {
 		for _, target := range ctx.MultiTargets() {
 			sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
@@ -900,14 +932,35 @@
 	j.extraResources = append(j.extraResources, p)
 }
 
+func (j *TestHost) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var configs []tradefed.Config
+	if len(j.testHostProperties.Data_device_bins) > 0 {
+		// add Tradefed configuration to push device bins to device for testing
+		remoteDir := filepath.Join("/data/local/tests/unrestricted/", j.Name())
+		options := []tradefed.Option{{Name: "cleanup", Value: "true"}}
+		for _, bin := range j.testHostProperties.Data_device_bins {
+			fullPath := filepath.Join(remoteDir, bin)
+			options = append(options, tradefed.Option{Name: "push-file", Key: bin, Value: fullPath})
+		}
+		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.PushFilePreparer", options})
+	}
+
+	j.Test.generateAndroidBuildActionsWithConfig(ctx, configs)
+}
+
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	j.generateAndroidBuildActionsWithConfig(ctx, nil)
+}
+
+func (j *Test) generateAndroidBuildActionsWithConfig(ctx android.ModuleContext, configs []tradefed.Config) {
 	if j.testProperties.Test_options.Unit_test == nil && ctx.Host() {
 		// TODO(b/): Clean temporary heuristic to avoid unexpected onboarding.
 		defaultUnitTest := !inList("tradefed", j.properties.Libs) && !inList("cts", j.testProperties.Test_suites)
 		j.testProperties.Test_options.Unit_test = proptools.BoolPtr(defaultUnitTest)
 	}
+
 	j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.testProperties.Test_config, j.testProperties.Test_config_template,
-		j.testProperties.Test_suites, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test)
+		j.testProperties.Test_suites, configs, j.testProperties.Auto_gen_config, j.testProperties.Test_options.Unit_test)
 
 	j.data = android.PathsForModuleSrc(ctx, j.testProperties.Data)
 
@@ -917,6 +970,10 @@
 		j.data = append(j.data, android.OutputFileForModule(ctx, dep, ""))
 	})
 
+	ctx.VisitDirectDepsWithTag(dataDeviceBinsTag, func(dep android.Module) {
+		j.data = append(j.data, android.OutputFileForModule(ctx, dep, ""))
+	})
+
 	ctx.VisitDirectDepsWithTag(jniLibTag, func(dep android.Module) {
 		sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo)
 		if sharedLibInfo.SharedLibrary != nil {
@@ -949,7 +1006,7 @@
 
 func (j *JavaTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	j.testConfig = tradefed.AutoGenJavaTestConfig(ctx, j.prebuiltTestProperties.Test_config, nil,
-		j.prebuiltTestProperties.Test_suites, nil, nil)
+		j.prebuiltTestProperties.Test_suites, nil, nil, nil)
 
 	j.Import.GenerateAndroidBuildActions(ctx)
 }
@@ -1207,6 +1264,8 @@
 
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommonFirst)
 	android.InitDefaultableModule(module)
+	android.InitBazelModule(module)
+
 	return module
 }
 
@@ -1224,6 +1283,7 @@
 
 	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommonFirst)
 	android.InitDefaultableModule(module)
+	android.InitBazelModule(module)
 	return module
 }
 
@@ -1273,6 +1333,7 @@
 	android.ModuleBase
 	android.DefaultableModuleBase
 	android.ApexModuleBase
+	android.BazelModuleBase
 	prebuilt android.Prebuilt
 	android.SdkBase
 
@@ -1628,6 +1689,7 @@
 	android.InitPrebuiltModule(module, &module.properties.Jars)
 	android.InitApexModule(module)
 	android.InitSdkAwareModule(module)
+	android.InitBazelModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	return module
 }
@@ -1840,6 +1902,7 @@
 	module.AddProperties(
 		&CommonProperties{},
 		&DeviceProperties{},
+		&OverridableDeviceProperties{},
 		&DexProperties{},
 		&DexpreoptProperties{},
 		&android.ProtoProperties{},
@@ -1940,3 +2003,139 @@
 		clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
 	}
 }
+
+type javaLibraryAttributes struct {
+	Srcs      bazel.LabelListAttribute
+	Deps      bazel.LabelListAttribute
+	Javacopts bazel.StringListAttribute
+}
+
+func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) *javaLibraryAttributes {
+	//TODO(b/209577426): Support multiple arch variants
+	srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs))
+	attrs := &javaLibraryAttributes{
+		Srcs: srcs,
+	}
+
+	if m.properties.Javacflags != nil {
+		attrs.Javacopts = bazel.MakeStringListAttribute(m.properties.Javacflags)
+	}
+
+	var deps bazel.LabelList
+	if m.properties.Libs != nil {
+		deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Libs))
+	}
+	if m.properties.Static_libs != nil {
+		//TODO(b/217236083) handle static libs similarly to Soong
+		deps.Append(android.BazelLabelForModuleDeps(ctx, m.properties.Static_libs))
+	}
+	attrs.Deps = bazel.MakeLabelListAttribute(deps)
+
+	return attrs
+}
+
+func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) {
+	attrs := m.convertLibraryAttrsBp2Build(ctx)
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "java_library",
+		Bzl_load_location: "//build/bazel/rules/java:library.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+}
+
+type javaBinaryHostAttributes struct {
+	Srcs       bazel.LabelListAttribute
+	Deps       bazel.LabelListAttribute
+	Main_class string
+	Jvm_flags  bazel.StringListAttribute
+	Javacopts  bazel.StringListAttribute
+}
+
+// JavaBinaryHostBp2Build is for java_binary_host bp2build.
+func javaBinaryHostBp2Build(ctx android.TopDownMutatorContext, m *Binary) {
+	mainClass := ""
+	if m.binaryProperties.Main_class != nil {
+		mainClass = *m.binaryProperties.Main_class
+	}
+	if m.properties.Manifest != nil {
+		mainClassInManifest, err := android.GetMainClassInManifest(ctx.Config(), android.PathForModuleSrc(ctx, *m.properties.Manifest).String())
+		if err != nil {
+			return
+		}
+		mainClass = mainClassInManifest
+	}
+	srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs))
+	attrs := &javaBinaryHostAttributes{
+		Srcs:       srcs,
+		Main_class: mainClass,
+	}
+
+	if m.properties.Javacflags != nil {
+		attrs.Javacopts = bazel.MakeStringListAttribute(m.properties.Javacflags)
+	}
+
+	// Attribute deps
+	deps := []string{}
+	if m.properties.Static_libs != nil {
+		deps = append(deps, m.properties.Static_libs...)
+	}
+	if m.binaryProperties.Jni_libs != nil {
+		deps = append(deps, m.binaryProperties.Jni_libs...)
+	}
+	if len(deps) > 0 {
+		attrs.Deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, deps))
+	}
+
+	// Attribute jvm_flags
+	if m.binaryProperties.Jni_libs != nil {
+		jniLibPackages := map[string]bool{}
+		for _, jniLibLabel := range android.BazelLabelForModuleDeps(ctx, m.binaryProperties.Jni_libs).Includes {
+			jniLibPackage := jniLibLabel.Label
+			indexOfColon := strings.Index(jniLibLabel.Label, ":")
+			if indexOfColon > 0 {
+				// JNI lib from other package
+				jniLibPackage = jniLibLabel.Label[2:indexOfColon]
+			} else if indexOfColon == 0 {
+				// JNI lib in the same package of java_binary
+				packageOfCurrentModule := m.GetBazelLabel(ctx, m)
+				jniLibPackage = packageOfCurrentModule[2:strings.Index(packageOfCurrentModule, ":")]
+			}
+			if _, inMap := jniLibPackages[jniLibPackage]; !inMap {
+				jniLibPackages[jniLibPackage] = true
+			}
+		}
+		jniLibPaths := []string{}
+		for jniLibPackage, _ := range jniLibPackages {
+			// See cs/f:.*/third_party/bazel/.*java_stub_template.txt for the use of RUNPATH
+			jniLibPaths = append(jniLibPaths, "$${RUNPATH}"+jniLibPackage)
+		}
+		attrs.Jvm_flags = bazel.MakeStringListAttribute([]string{"-Djava.library.path=" + strings.Join(jniLibPaths, ":")})
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class: "java_binary",
+	}
+
+	// Create the BazelTargetModule.
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+}
+
+type bazelJavaImportAttributes struct {
+	Jars bazel.LabelListAttribute
+}
+
+// java_import bp2Build converter.
+func (i *Import) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	//TODO(b/209577426): Support multiple arch variants
+	jars := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, i.properties.Jars, []string(nil)))
+
+	attrs := &bazelJavaImportAttributes{
+		Jars: jars,
+	}
+	props := bazel.BazelTargetModuleProperties{Rule_class: "java_import"}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: android.RemoveOptionalPrebuiltPrefix(i.Name())}, attrs)
+
+}
diff --git a/java/java_test.go b/java/java_test.go
index 6e4e673..21c76b6 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -997,60 +997,6 @@
 	}
 }
 
-func TestJarGenrules(t *testing.T) {
-	ctx, _ := testJava(t, `
-		java_library {
-			name: "foo",
-			srcs: ["a.java"],
-		}
-
-		java_genrule {
-			name: "jargen",
-			tool_files: ["b.java"],
-			cmd: "$(location b.java) $(in) $(out)",
-			out: ["jargen.jar"],
-			srcs: [":foo"],
-		}
-
-		java_library {
-			name: "bar",
-			static_libs: ["jargen"],
-			srcs: ["c.java"],
-		}
-
-		java_library {
-			name: "baz",
-			libs: ["jargen"],
-			srcs: ["c.java"],
-		}
-	`)
-
-	foo := ctx.ModuleForTests("foo", "android_common").Output("javac/foo.jar")
-	jargen := ctx.ModuleForTests("jargen", "android_common").Output("jargen.jar")
-	bar := ctx.ModuleForTests("bar", "android_common").Output("javac/bar.jar")
-	baz := ctx.ModuleForTests("baz", "android_common").Output("javac/baz.jar")
-	barCombined := ctx.ModuleForTests("bar", "android_common").Output("combined/bar.jar")
-
-	if g, w := jargen.Implicits.Strings(), foo.Output.String(); !android.InList(w, g) {
-		t.Errorf("expected jargen inputs [%q], got %q", w, g)
-	}
-
-	if !strings.Contains(bar.Args["classpath"], jargen.Output.String()) {
-		t.Errorf("bar classpath %v does not contain %q", bar.Args["classpath"], jargen.Output.String())
-	}
-
-	if !strings.Contains(baz.Args["classpath"], jargen.Output.String()) {
-		t.Errorf("baz classpath %v does not contain %q", baz.Args["classpath"], jargen.Output.String())
-	}
-
-	if len(barCombined.Inputs) != 2 ||
-		barCombined.Inputs[0].String() != bar.Output.String() ||
-		barCombined.Inputs[1].String() != jargen.Output.String() {
-		t.Errorf("bar combined jar inputs %v is not [%q, %q]",
-			barCombined.Inputs.Strings(), bar.Output.String(), jargen.Output.String())
-	}
-}
-
 func TestExcludeFileGroupInSrcs(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_library {
@@ -1514,3 +1460,64 @@
 		t.Errorf("expected errorprone to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
 	}
 }
+
+func TestDataDeviceBinsBuildsDeviceBinary(t *testing.T) {
+	bp := `
+		java_test_host {
+			name: "foo",
+			srcs: ["test.java"],
+			data_device_bins: ["bar"],
+		}
+
+		cc_binary {
+			name: "bar",
+		}
+	`
+
+	ctx := android.GroupFixturePreparers(
+		PrepareForIntegrationTestWithJava,
+	).RunTestWithBp(t, bp)
+
+	buildOS := ctx.Config.BuildOS.String()
+	fooVariant := ctx.ModuleForTests("foo", buildOS+"_common")
+	barVariant := ctx.ModuleForTests("bar", "android_arm64_armv8-a")
+	fooMod := fooVariant.Module().(*TestHost)
+
+	relocated := barVariant.Output("bar")
+	expectedInput := "out/soong/.intermediates/bar/android_arm64_armv8-a/unstripped/bar"
+	android.AssertPathRelativeToTopEquals(t, "relocation input", expectedInput, relocated.Input)
+
+	entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, fooMod)[0]
+	expectedData := []string{
+		"out/soong/.intermediates/bar/android_arm64_armv8-a/bar:bar",
+	}
+	actualData := entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"]
+	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", ctx.Config, expectedData, actualData)
+}
+
+func TestDataDeviceBinsAutogenTradefedConfig(t *testing.T) {
+	bp := `
+		java_test_host {
+			name: "foo",
+			srcs: ["test.java"],
+			data_device_bins: ["bar"],
+		}
+
+		cc_binary {
+			name: "bar",
+		}
+	`
+
+	ctx := android.GroupFixturePreparers(
+		PrepareForIntegrationTestWithJava,
+	).RunTestWithBp(t, bp)
+
+	buildOS := ctx.Config.BuildOS.String()
+	fooModule := ctx.ModuleForTests("foo", buildOS+"_common")
+	expectedAutogenConfig := `<option name="push-file" key="bar" value="/data/local/tests/unrestricted/foo/bar" />`
+
+	autogen := fooModule.Rule("autogen")
+	if !strings.Contains(autogen.Args["extraConfigs"], expectedAutogenConfig) {
+		t.Errorf("foo extraConfigs %v does not contain %q", autogen.Args["extraConfigs"], expectedAutogenConfig)
+	}
+}
diff --git a/java/lint.go b/java/lint.go
index fe3218e..7845c33 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -377,6 +377,7 @@
 	html := android.PathForModuleOut(ctx, "lint", "lint-report.html")
 	text := android.PathForModuleOut(ctx, "lint", "lint-report.txt")
 	xml := android.PathForModuleOut(ctx, "lint", "lint-report.xml")
+	baseline := android.PathForModuleOut(ctx, "lint", "lint-baseline.xml")
 
 	depSetsBuilder := NewLintDepSetBuilder().Direct(html, text, xml)
 
@@ -447,6 +448,8 @@
 		cmd.FlagWithInput("--baseline ", lintBaseline.Path())
 	}
 
+	cmd.FlagWithOutput("--write-reference-baseline ", baseline)
+
 	cmd.Text("|| (").Text("if [ -e").Input(text).Text("]; then cat").Input(text).Text("; fi; exit 7)")
 
 	rule.Command().Text("rm -rf").Flag(lintPaths.cacheDir.String()).Flag(lintPaths.homeDir.String())
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 0d8ebac..f442ddf 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -115,7 +115,7 @@
 		Include:    "$(BUILD_PREBUILT)",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
 			},
 		},
diff --git a/java/plugin.go b/java/plugin.go
index 297ac2c..4b174b9 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -14,7 +14,10 @@
 
 package java
 
-import "android/soong/android"
+import (
+	"android/soong/android"
+	"android/soong/bazel"
+)
 
 func init() {
 	registerJavaPluginBuildComponents(android.InitRegistrationContext)
@@ -24,7 +27,6 @@
 	ctx.RegisterModuleType("java_plugin", PluginFactory)
 }
 
-// A java_plugin module describes a host java library that will be used by javac as an annotation processor.
 func PluginFactory() android.Module {
 	module := &Plugin{}
 
@@ -32,9 +34,13 @@
 	module.AddProperties(&module.pluginProperties)
 
 	InitJavaModule(module, android.HostSupported)
+
+	android.InitBazelModule(module)
+
 	return module
 }
 
+// Plugin describes a java_plugin module, a host java library that will be used by javac as an annotation processor.
 type Plugin struct {
 	Library
 
@@ -50,3 +56,29 @@
 	// parallelism and cause more recompilation for modules that depend on modules that use this plugin.
 	Generates_api *bool
 }
+
+type pluginAttributes struct {
+	*javaLibraryAttributes
+	Processor_class        *string
+	Target_compatible_with bazel.LabelListAttribute
+}
+
+// ConvertWithBp2build is used to convert android_app to Bazel.
+func (p *Plugin) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	libAttrs := p.convertLibraryAttrsBp2Build(ctx)
+	attrs := &pluginAttributes{
+		libAttrs,
+		nil,
+		bazel.LabelListAttribute{},
+	}
+
+	if p.pluginProperties.Processor_class != nil {
+		attrs.Processor_class = p.pluginProperties.Processor_class
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class: "java_plugin",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: p.Name()}, attrs)
+}
diff --git a/java/proto.go b/java/proto.go
index ab913d8..8d23803 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -73,13 +73,15 @@
 }
 
 func protoDeps(ctx android.BottomUpMutatorContext, p *android.ProtoProperties) {
+	const unspecifiedProtobufPluginType = ""
 	if String(p.Proto.Plugin) == "" {
 		switch String(p.Proto.Type) {
+		case "stream": // does not require additional dependencies
 		case "micro":
 			ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-micro")
 		case "nano":
 			ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-nano")
-		case "lite", "":
+		case "lite", unspecifiedProtobufPluginType:
 			ctx.AddVariationDependencies(nil, staticLibTag, "libprotobuf-java-lite")
 		case "full":
 			if ctx.Host() || ctx.BazelConversionMode() {
diff --git a/java/proto_test.go b/java/proto_test.go
new file mode 100644
index 0000000..d1cb714
--- /dev/null
+++ b/java/proto_test.go
@@ -0,0 +1,53 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+const protoModules = `
+java_library_static {
+    name: "libprotobuf-java-lite",
+}
+`
+
+func TestProtoStream(t *testing.T) {
+	bp := `
+		java_library {
+			name: "java-stream-protos",
+			proto: {
+				type: "stream",
+			},
+			srcs: [
+				"a.proto",
+				"b.proto",
+			],
+		}
+	`
+
+	ctx := android.GroupFixturePreparers(
+		PrepareForIntegrationTestWithJava,
+	).RunTestWithBp(t, protoModules+bp)
+
+	proto0 := ctx.ModuleForTests("java-stream-protos", "android_common").Output("proto/proto0.srcjar")
+
+	if cmd := proto0.RuleParams.Command; !strings.Contains(cmd, "--javastream_out=") {
+		t.Errorf("expected '--javastream_out' in %q", cmd)
+	}
+}
diff --git a/java/robolectric.go b/java/robolectric.go
index 16af546..f719521 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -352,7 +352,6 @@
 	return module
 }
 
-func (r *robolectricTest) InstallBypassMake() bool  { return true }
 func (r *robolectricTest) InstallInTestcases() bool { return true }
 func (r *robolectricTest) InstallForceOS() (*android.OsType, *android.ArchType) {
 	return &r.forceOSType, &r.forceArchType
@@ -430,7 +429,6 @@
 	}
 }
 
-func (r *robolectricRuntimes) InstallBypassMake() bool  { return true }
 func (r *robolectricRuntimes) InstallInTestcases() bool { return true }
 func (r *robolectricRuntimes) InstallForceOS() (*android.OsType, *android.ArchType) {
 	return &r.forceOSType, &r.forceArchType
diff --git a/java/rro.go b/java/rro.go
index 0b4d091..be84aff 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -139,7 +139,7 @@
 		aaptLinkFlags = append(aaptLinkFlags,
 			"--rename-overlay-target-package "+*r.overridableProperties.Target_package_name)
 	}
-	r.aapt.buildActions(ctx, r, nil, aaptLinkFlags...)
+	r.aapt.buildActions(ctx, r, nil, nil, aaptLinkFlags...)
 
 	// Sign the built package
 	_, certificates := collectAppDeps(ctx, r, false, false)
diff --git a/java/rro_test.go b/java/rro_test.go
index 27abbe4..be0d7ba 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -62,6 +62,7 @@
 	result := android.GroupFixturePreparers(
 		PrepareForTestWithJavaDefaultModules,
 		PrepareForTestWithOverlayBuildComponents,
+		android.FixtureModifyConfig(android.SetKatiEnabledForTests),
 		fs.AddToFixture(),
 	).RunTestWithBp(t, bp)
 
@@ -127,7 +128,10 @@
 }
 
 func TestRuntimeResourceOverlay_JavaDefaults(t *testing.T) {
-	ctx, config := testJava(t, `
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.FixtureModifyConfig(android.SetKatiEnabledForTests),
+	).RunTestWithBp(t, `
 		java_defaults {
 			name: "rro_defaults",
 			theme: "default_theme",
@@ -148,7 +152,7 @@
 	//
 	// RRO module with defaults
 	//
-	m := ctx.ModuleForTests("foo_with_defaults", "android_common")
+	m := result.ModuleForTests("foo_with_defaults", "android_common")
 
 	// Check AAPT2 link flags.
 	aapt2Flags := strings.Split(m.Output("package-res.apk").Args["flags"], " ")
@@ -159,14 +163,14 @@
 	}
 
 	// Check device location.
-	path := android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+	path := android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
 	expectedPath := []string{shared.JoinPath("out/target/product/test_device/product/overlay/default_theme")}
-	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
+	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
 
 	//
 	// RRO module without defaults
 	//
-	m = ctx.ModuleForTests("foo_barebones", "android_common")
+	m = result.ModuleForTests("foo_barebones", "android_common")
 
 	// Check AAPT2 link flags.
 	aapt2Flags = strings.Split(m.Output("package-res.apk").Args["flags"], " ")
@@ -176,9 +180,9 @@
 	}
 
 	// Check device location.
-	path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
+	path = android.AndroidMkEntriesForTest(t, result.TestContext, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
 	expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")}
-	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
+	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", result.Config, expectedPath, path)
 }
 
 func TestOverrideRuntimeResourceOverlay(t *testing.T) {
diff --git a/java/sdk.go b/java/sdk.go
index e6bf220..0dddd40 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -55,8 +55,10 @@
 		return JAVA_VERSION_7
 	} else if sdk.FinalOrFutureInt() <= 29 {
 		return JAVA_VERSION_8
-	} else {
+	} else if sdk.FinalOrFutureInt() <= 31 {
 		return JAVA_VERSION_9
+	} else {
+		return JAVA_VERSION_11
 	}
 }
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 3065d57..e794a48 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -21,7 +21,6 @@
 	"reflect"
 	"regexp"
 	"sort"
-	"strconv"
 	"strings"
 	"sync"
 
@@ -1129,6 +1128,22 @@
 	return generatedScopes
 }
 
+var _ android.ModuleWithMinSdkVersionCheck = (*SdkLibrary)(nil)
+
+func (module *SdkLibrary) CheckMinSdkVersion(ctx android.ModuleContext) {
+	android.CheckMinSdkVersion(ctx, module.MinSdkVersion(ctx).ApiLevel, func(c android.ModuleContext, do android.PayloadDepsCallback) {
+		ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
+			isExternal := !module.depIsInSameApex(ctx, child)
+			if am, ok := child.(android.ApexModule); ok {
+				if !do(ctx, parent, am, isExternal) {
+					return false
+				}
+			}
+			return !isExternal
+		})
+	})
+}
+
 type sdkLibraryComponentTag struct {
 	blueprint.BaseDependencyTag
 	name string
@@ -1206,14 +1221,23 @@
 
 func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) {
 	paths, err := module.commonOutputFiles(tag)
-	if paths == nil && err == nil {
-		return module.Library.OutputFiles(tag)
-	} else {
+	if paths != nil || err != nil {
 		return paths, err
 	}
+	if module.requiresRuntimeImplementationLibrary() {
+		return module.Library.OutputFiles(tag)
+	}
+	if tag == "" {
+		return nil, nil
+	}
+	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 }
 
 func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if proptools.String(module.deviceProperties.Min_sdk_version) != "" {
+		module.CheckMinSdkVersion(ctx)
+	}
+
 	module.generateCommonBuildActions(ctx)
 
 	// Only build an implementation library if required.
@@ -2526,8 +2550,14 @@
 		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"), err.Error())
 		return ""
 	}
-	intStr := strconv.Itoa(apiLevel.FinalOrPreviewInt())
-	return formattedOptionalAttribute(attrName, &intStr)
+	if apiLevel.IsCurrent() {
+		// passing "current" would always mean a future release, never the current (or the current in
+		// progress) which means some conditions would never be triggered.
+		ctx.PropertyErrorf(strings.ReplaceAll(attrName, "-", "_"),
+			`"current" is not an allowed value for this attribute`)
+		return ""
+	}
+	return formattedOptionalAttribute(attrName, value)
 }
 
 // formats an attribute for the xml permissions file if the value is not null
@@ -2548,11 +2578,11 @@
 	implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before)
 	minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk)
 	maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk)
-	// <library> is understood in all android versions whereas <updatable-library> is only understood from API T (and ignored before that).
-	// similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the updatable-library to make sure this library is not loaded before T
+	// <library> is understood in all android versions whereas <apex-library> is only understood from API T (and ignored before that).
+	// similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T
 	var libraryTag string
 	if module.properties.Min_device_sdk != nil {
-		libraryTag = `    <updatable-library\n`
+		libraryTag = `    <apex-library\n`
 	} else {
 		libraryTag = `    <library\n`
 	}
@@ -2605,18 +2635,18 @@
 
 func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
 	if module.hideApexVariantFromMake {
-		return []android.AndroidMkEntries{android.AndroidMkEntries{
+		return []android.AndroidMkEntries{{
 			Disabled: true,
 		}}
 	}
 
-	return []android.AndroidMkEntries{android.AndroidMkEntries{
+	return []android.AndroidMkEntries{{
 		Class:      "ETC",
 		OutputFile: android.OptionalPathForPath(module.outputFilePath),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				entries.SetString("LOCAL_MODULE_TAGS", "optional")
-				entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base())
 			},
 		},
@@ -2730,7 +2760,7 @@
 	android.SdkMemberPropertiesBase
 
 	// Scope to per scope properties.
-	Scopes map[*apiScope]scopeProperties
+	Scopes map[*apiScope]*scopeProperties
 
 	// The Java stubs source files.
 	Stub_srcs []string
@@ -2783,14 +2813,14 @@
 	StubsSrcJar    android.Path
 	CurrentApiFile android.Path
 	RemovedApiFile android.Path
-	AnnotationsZip android.Path
+	AnnotationsZip android.Path `supported_build_releases:"Tiramisu+"`
 	SdkVersion     string
 }
 
 func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
 	sdk := variant.(*SdkLibrary)
 
-	s.Scopes = make(map[*apiScope]scopeProperties)
+	s.Scopes = make(map[*apiScope]*scopeProperties)
 	for _, apiScope := range allApiScopes {
 		paths := sdk.findScopePaths(apiScope)
 		if paths == nil {
@@ -2813,7 +2843,7 @@
 			if paths.annotationsZip.Valid() {
 				properties.AnnotationsZip = paths.annotationsZip.Path()
 			}
-			s.Scopes[apiScope] = properties
+			s.Scopes[apiScope] = &properties
 		}
 	}
 
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 2271573..3500c84 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -48,6 +48,7 @@
 			name: "bar",
 			srcs: ["a.java", "b.java"],
 			api_packages: ["bar"],
+			exclude_kotlinc_generated_files: true,
 		}
 		java_library {
 			name: "baz",
@@ -161,6 +162,14 @@
 		android.AssertDeepEquals(t, "qux exports (required)", []string{"fred", "quuz", "foo", "bar"}, requiredSdkLibs)
 		android.AssertDeepEquals(t, "qux exports (optional)", []string{}, optionalSdkLibs)
 	}
+
+	fooDexJar := result.ModuleForTests("foo", "android_common").Rule("d8")
+	// tests if kotlinc generated files are NOT excluded from output of foo.
+	android.AssertStringDoesNotContain(t, "foo dex", fooDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
+
+	barDexJar := result.ModuleForTests("bar", "android_common").Rule("d8")
+	// tests if kotlinc generated files are excluded from output of bar.
+	android.AssertStringDoesContain(t, "bar dex", barDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) {
@@ -173,7 +182,7 @@
 			"30": {"foo", "fooUpdatable", "fooUpdatableErr"},
 		}),
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W"}
+			variables.Platform_version_active_codenames = []string{"Tiramisu", "U", "V", "W", "X"}
 		}),
 	).RunTestWithBp(t,
 		`
@@ -184,7 +193,7 @@
 			on_bootclasspath_since: "U",
 			on_bootclasspath_before: "V",
 			min_device_sdk: "W",
-			max_device_sdk: "current",
+			max_device_sdk: "X",
 			min_sdk_version: "S",
 		}
 		java_sdk_library {
@@ -193,12 +202,13 @@
 			api_packages: ["foo"],
 		}
 `)
+
 	// test that updatability attributes are passed on correctly
 	fooUpdatable := result.ModuleForTests("fooUpdatable.xml", "android_common").Rule("java_sdk_xml")
-	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"9001\"`)
-	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"9002\"`)
-	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"9003\"`)
-	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"10000\"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-since=\"U\"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `on-bootclasspath-before=\"V\"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `min-device-sdk=\"W\"`)
+	android.AssertStringDoesContain(t, "fooUpdatable.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `max-device-sdk=\"X\"`)
 
 	// double check that updatability attributes are not written if they don't exist in the bp file
 	// the permissions file for the foo library defined above
@@ -221,7 +231,7 @@
 			`on_bootclasspath_since: "aaa" could not be parsed as an integer and is not a recognized codename`,
 			`on_bootclasspath_before: "bbc" could not be parsed as an integer and is not a recognized codename`,
 			`min_device_sdk: "ccc" could not be parsed as an integer and is not a recognized codename`,
-			`max_device_sdk: "ddd" could not be parsed as an integer and is not a recognized codename`,
+			`max_device_sdk: "current" is not an allowed value for this attribute`,
 		})).RunTestWithBp(t,
 		`
 	java_sdk_library {
@@ -231,7 +241,7 @@
 			on_bootclasspath_since: "aaa",
 			on_bootclasspath_before: "bbc",
 			min_device_sdk: "ccc",
-			max_device_sdk: "ddd",
+			max_device_sdk: "current",
 		}
 `)
 }
@@ -338,7 +348,7 @@
 `)
 	// test that updatability attributes are passed on correctly
 	fooUpdatable := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml")
-	android.AssertStringDoesContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<updatable-library`)
+	android.AssertStringDoesContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<apex-library`)
 	android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<library`)
 }
 
@@ -1140,3 +1150,87 @@
 		})
 	}
 }
+
+func TestSdkLibrary_CheckMinSdkVersion(t *testing.T) {
+	preparer := android.GroupFixturePreparers(
+		PrepareForTestWithJavaBuildComponents,
+		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithJavaSdkLibraryFiles,
+	)
+
+	preparer.RunTestWithBp(t, `
+		java_sdk_library {
+			name: "sdklib",
+            srcs: ["a.java"],
+            static_libs: ["util"],
+            min_sdk_version: "30",
+			unsafe_ignore_missing_latest_api: true,
+        }
+
+		java_library {
+			name: "util",
+			srcs: ["a.java"],
+			min_sdk_version: "30",
+		}
+	`)
+
+	preparer.
+		RunTestWithBp(t, `
+			java_sdk_library {
+				name: "sdklib",
+				srcs: ["a.java"],
+				libs: ["util"],
+				impl_only_libs: ["util"],
+				stub_only_libs: ["util"],
+				stub_only_static_libs: ["util"],
+				min_sdk_version: "30",
+				unsafe_ignore_missing_latest_api: true,
+			}
+
+			java_library {
+				name: "util",
+				srcs: ["a.java"],
+			}
+		`)
+
+	preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "util".*should support min_sdk_version\(30\)`)).
+		RunTestWithBp(t, `
+			java_sdk_library {
+				name: "sdklib",
+				srcs: ["a.java"],
+				static_libs: ["util"],
+				min_sdk_version: "30",
+				unsafe_ignore_missing_latest_api: true,
+			}
+
+			java_library {
+				name: "util",
+				srcs: ["a.java"],
+				min_sdk_version: "31",
+			}
+		`)
+
+	preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module "another_util".*should support min_sdk_version\(30\)`)).
+		RunTestWithBp(t, `
+			java_sdk_library {
+				name: "sdklib",
+				srcs: ["a.java"],
+				static_libs: ["util"],
+				min_sdk_version: "30",
+				unsafe_ignore_missing_latest_api: true,
+			}
+
+			java_library {
+				name: "util",
+				srcs: ["a.java"],
+				static_libs: ["another_util"],
+				min_sdk_version: "30",
+			}
+
+			java_library {
+				name: "another_util",
+				srcs: ["a.java"],
+				min_sdk_version: "31",
+			}
+		`)
+}
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 2ec33a4..fa61ea6 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -60,7 +60,7 @@
 	classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, p.classpathType)
 	standaloneConfiguredJars := p.standaloneConfiguredJars(ctx)
 	standaloneClasspathJars := configuredJarListToClasspathJars(ctx, standaloneConfiguredJars, STANDALONE_SYSTEMSERVER_JARS)
-	configuredJars = configuredJars.AppendList(standaloneConfiguredJars)
+	configuredJars = configuredJars.AppendList(&standaloneConfiguredJars)
 	classpathJars = append(classpathJars, standaloneClasspathJars...)
 	p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
 }
@@ -122,7 +122,7 @@
 	classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, s.classpathType)
 	standaloneConfiguredJars := s.standaloneConfiguredJars(ctx)
 	standaloneClasspathJars := configuredJarListToClasspathJars(ctx, standaloneConfiguredJars, STANDALONE_SYSTEMSERVER_JARS)
-	configuredJars = configuredJars.AppendList(standaloneConfiguredJars)
+	configuredJars = configuredJars.AppendList(&standaloneConfiguredJars)
 	classpathJars = append(classpathJars, standaloneClasspathJars...)
 	s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
 
diff --git a/java/testing.go b/java/testing.go
index 7441e44..6c49bc8 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -506,3 +506,19 @@
 		}
 	}
 }
+
+// Applies the given modifier on the boot image config with the given name.
+func FixtureModifyBootImageConfig(name string, configModifier func(*bootImageConfig)) android.FixturePreparer {
+	return android.FixtureModifyConfig(func(androidConfig android.Config) {
+		pathCtx := android.PathContextForTesting(androidConfig)
+		config := genBootImageConfigRaw(pathCtx)
+		configModifier(config[name])
+	})
+}
+
+// Sets the value of `installDirOnDevice` of the boot image config with the given name.
+func FixtureSetBootImageInstallDirOnDevice(name string, installDir string) android.FixturePreparer {
+	return FixtureModifyBootImageConfig(name, func(config *bootImageConfig) {
+		config.installDirOnDevice = installDir
+	})
+}
diff --git a/licenses/Android.bp b/licenses/Android.bp
index 5b764dc..8db001f 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -18,6 +18,11 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+filegroup {
+    name: "Apache-2.0-License-Text",
+    srcs: ["LICENSE"],
+}
+
 license {
     name: "Android-Apache-2.0",
     package_name: "Android",
diff --git a/licenses/LICENSE b/licenses/LICENSE
index dae0406..d645695 100644
--- a/licenses/LICENSE
+++ b/licenses/LICENSE
@@ -1,16 +1,4 @@
 
-   Copyright (c) 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.
-
-   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.
-
-
                                  Apache License
                            Version 2.0, January 2004
                         http://www.apache.org/licenses/
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 8d0ad7c..dbc112e 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -155,7 +155,7 @@
 		OutputFile: android.OptionalPathForPath(l.outputFilePath),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", l.installDirPath.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_PATH", l.installDirPath.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", l.outputFilePath.Base())
 				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !installable)
 				entries.SetString("LINKER_CONFIG_PATH_"+l.Name(), l.OutputFile().String())
diff --git a/linkerconfig/proto/linker_config.proto b/linkerconfig/proto/linker_config.proto
index fec66c8..dccf311 100644
--- a/linkerconfig/proto/linker_config.proto
+++ b/linkerconfig/proto/linker_config.proto
@@ -34,4 +34,16 @@
 
   // Required libs from the module
   repeated string requireLibs = 4;
+
+  message Contribution {
+    // Target namespace where this module contributes the search paths.
+    string namespace = 1;
+    // Search paths (inc. permitted paths) that this module contributes.
+    // Paths should be related to the current module and can use "${LIB}" variable which is
+    // expanded to "lib" or "lib64".
+    // e.g. ${LIB}/subdir
+    repeated string paths = 2;
+  }
+  // APEX can contribute search paths to specified namespaces.
+  repeated Contribution contributions = 5;
 }
diff --git a/mk2rbc/Android.bp b/mk2rbc/Android.bp
index b18bfc7..4fa3eb6 100644
--- a/mk2rbc/Android.bp
+++ b/mk2rbc/Android.bp
@@ -38,7 +38,6 @@
         "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 bb5a680..e84eacd 100644
--- a/mk2rbc/cmd/mk2rbc.go
+++ b/mk2rbc/cmd/mk2rbc.go
@@ -40,7 +40,6 @@
 )
 
 var (
-	rootDir = flag.String("root", ".", "the value of // for load paths")
 	// TODO(asmundak): remove this option once there is a consensus on suffix
 	suffix   = flag.String("suffix", ".rbc", "generated files' suffix")
 	dryRun   = flag.Bool("dry_run", false, "dry run")
@@ -57,6 +56,7 @@
 	cpuProfile            = flag.String("cpu_profile", "", "write cpu profile to file")
 	traceCalls            = flag.Bool("trace_calls", false, "trace function calls")
 	inputVariables        = flag.String("input_variables", "", "starlark file containing product config and global variables")
+	makefileList          = flag.String("makefile_list", "", "path to a list of all makefiles in the source tree, generated by soong's finder. If not provided, mk2rbc will find the makefiles itself (more slowly than if this flag was provided)")
 )
 
 func init() {
@@ -70,7 +70,6 @@
 		quit("cannot alias unknown flag " + target)
 	}
 	flagAlias("suffix", "s")
-	flagAlias("root", "d")
 	flagAlias("dry_run", "n")
 	flagAlias("convert_dependents", "r")
 	flagAlias("error_stat", "e")
@@ -79,8 +78,7 @@
 var backupSuffix string
 var tracedVariables []string
 var errorLogger = errorSink{data: make(map[string]datum)}
-var makefileFinder = &LinuxMakefileFinder{}
-var versionDefaultsMk = filepath.Join("build", "make", "core", "version_defaults.mk")
+var makefileFinder mk2rbc.MakefileFinder
 
 func main() {
 	flag.Usage = func() {
@@ -91,6 +89,10 @@
 	}
 	flag.Parse()
 
+	if _, err := os.Stat("build/soong/mk2rbc"); err != nil {
+		quit("Must be run from the root of the android tree. (build/soong/mk2rbc does not exist)")
+	}
+
 	// Delouse
 	if *suffix == ".mk" {
 		quit("cannot use .mk as generated file suffix")
@@ -134,6 +136,16 @@
 		pprof.StartCPUProfile(f)
 		defer pprof.StopCPUProfile()
 	}
+
+	if *makefileList != "" {
+		makefileFinder = &FileListMakefileFinder{
+			cachedMakefiles: nil,
+			filePath:        *makefileList,
+		}
+	} else {
+		makefileFinder = &FindCommandMakefileFinder{}
+	}
+
 	// Find out global variables
 	getConfigVariables()
 	getSoongVariables()
@@ -168,18 +180,14 @@
 		if len(files) != 1 {
 			quit(fmt.Errorf("a launcher can be generated only for a single product"))
 		}
-		versionDefaults, err := generateVersionDefaults()
-		if err != nil {
-			quit(err)
+		if *inputVariables == "" {
+			quit(fmt.Errorf("the product launcher requires an input variables file"))
 		}
-		versionDefaultsPath := outputFilePath(versionDefaultsMk)
-		err = writeGenerated(versionDefaultsPath, versionDefaults)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "%s: %s", files[0], err)
-			ok = false
+		if !convertOne(*inputVariables) {
+			quit(fmt.Errorf("the product launcher input variables file failed to convert"))
 		}
 
-		err = writeGenerated(*launcher, mk2rbc.Launcher(outputFilePath(files[0]), versionDefaultsPath,
+		err := writeGenerated(*launcher, mk2rbc.Launcher(outputFilePath(files[0]), outputFilePath(*inputVariables),
 			mk2rbc.MakePath2ModuleName(files[0])))
 		if err != nil {
 			fmt.Fprintf(os.Stderr, "%s: %s", files[0], err)
@@ -213,15 +221,6 @@
 	}
 }
 
-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)
@@ -231,17 +230,16 @@
 	const androidProductsMk = "AndroidProducts.mk"
 	// Build the list of AndroidProducts.mk files: it's
 	// build/make/target/product/AndroidProducts.mk + device/**/AndroidProducts.mk plus + vendor/**/AndroidProducts.mk
-	targetAndroidProductsFile := filepath.Join(*rootDir, "build", "make", "target", "product", androidProductsMk)
+	targetAndroidProductsFile := filepath.Join("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",
-			targetAndroidProductsFile, err, *rootDir)
+		fmt.Fprintf(os.Stderr, "%s: %s\n", targetAndroidProductsFile, err)
 	}
 	productConfigMap := make(map[string]string)
 	if err := mk2rbc.UpdateProductConfigMap(productConfigMap, targetAndroidProductsFile); err != nil {
 		fmt.Fprintf(os.Stderr, "%s: %s\n", targetAndroidProductsFile, err)
 	}
 	for _, t := range []string{"device", "vendor"} {
-		_ = filepath.WalkDir(filepath.Join(*rootDir, t),
+		_ = filepath.WalkDir(t,
 			func(path string, d os.DirEntry, err error) error {
 				if err != nil || d.IsDir() || filepath.Base(path) != androidProductsMk {
 					return nil
@@ -257,10 +255,9 @@
 }
 
 func getConfigVariables() {
-	path := filepath.Join(*rootDir, "build", "make", "core", "product.mk")
+	path := filepath.Join("build", "make", "core", "product.mk")
 	if err := mk2rbc.FindConfigVariables(path, mk2rbc.KnownVariables); err != nil {
-		quit(fmt.Errorf("%s\n(check --root[=%s], it should point to the source root)",
-			err, *rootDir))
+		quit(err)
 	}
 }
 
@@ -273,11 +270,11 @@
 	if name != "BUILD_SYSTEM" {
 		return fmt.Sprintf("$(%s)", name)
 	}
-	return filepath.Join(*rootDir, "build", "make", "core")
+	return filepath.Join("build", "make", "core")
 }
 
 func getSoongVariables() {
-	path := filepath.Join(*rootDir, "build", "make", "core", "soong_config.mk")
+	path := filepath.Join("build", "make", "core", "soong_config.mk")
 	err := mk2rbc.FindSoongVariables(path, fileNameScope{}, mk2rbc.KnownVariables)
 	if err != nil {
 		quit(err)
@@ -328,12 +325,11 @@
 	mk2starRequest := mk2rbc.Request{
 		MkFile:          mkFile,
 		Reader:          nil,
-		RootDir:         *rootDir,
 		OutputDir:       *outputTop,
 		OutputSuffix:    *suffix,
 		TracedVariables: tracedVariables,
 		TraceCalls:      *traceCalls,
-		SourceFS:        os.DirFS(*rootDir),
+		SourceFS:        os.DirFS("."),
 		MakefileFinder:  makefileFinder,
 		ErrorLogger:     errorLogger,
 	}
@@ -533,17 +529,17 @@
 	return res, len(sorted)
 }
 
-type LinuxMakefileFinder struct {
+// FindCommandMakefileFinder is an implementation of mk2rbc.MakefileFinder that
+// runs the unix find command to find all the makefiles in the source tree.
+type FindCommandMakefileFinder struct {
 	cachedRoot      string
 	cachedMakefiles []string
 }
 
-func (l *LinuxMakefileFinder) Find(root string) []string {
+func (l *FindCommandMakefileFinder) Find(root string) []string {
 	if l.cachedMakefiles != nil && l.cachedRoot == root {
 		return l.cachedMakefiles
 	}
-	l.cachedRoot = root
-	l.cachedMakefiles = make([]string, 0)
 
 	// Return all *.mk files but not in hidden directories.
 
@@ -562,9 +558,60 @@
 		panic(fmt.Errorf("cannot get the output from %s: %s", cmd, err))
 	}
 	scanner := bufio.NewScanner(stdout)
+	result := make([]string, 0)
 	for scanner.Scan() {
-		l.cachedMakefiles = append(l.cachedMakefiles, strings.TrimPrefix(scanner.Text(), "./"))
+		result = append(result, strings.TrimPrefix(scanner.Text(), "./"))
 	}
 	stdout.Close()
+	err = scanner.Err()
+	if err != nil {
+		panic(fmt.Errorf("cannot get the output from %s: %s", cmd, err))
+	}
+	l.cachedRoot = root
+	l.cachedMakefiles = result
+	return l.cachedMakefiles
+}
+
+// FileListMakefileFinder is an implementation of mk2rbc.MakefileFinder that
+// reads a file containing the list of makefiles in the android source tree.
+// This file is generated by soong's finder, so that it can be computed while
+// soong is already walking the source tree looking for other files. If the root
+// to find makefiles under is not the root of the android source tree, it will
+// fall back to using FindCommandMakefileFinder.
+type FileListMakefileFinder struct {
+	FindCommandMakefileFinder
+	cachedMakefiles []string
+	filePath        string
+}
+
+func (l *FileListMakefileFinder) Find(root string) []string {
+	root, err1 := filepath.Abs(root)
+	wd, err2 := os.Getwd()
+	if root != wd || err1 != nil || err2 != nil {
+		return l.FindCommandMakefileFinder.Find(root)
+	}
+	if l.cachedMakefiles != nil {
+		return l.cachedMakefiles
+	}
+
+	file, err := os.Open(l.filePath)
+	if err != nil {
+		panic(fmt.Errorf("Cannot read makefile list: %s\n", err))
+	}
+	defer file.Close()
+
+	result := make([]string, 0)
+	scanner := bufio.NewScanner(file)
+	for scanner.Scan() {
+		line := scanner.Text()
+		if len(line) > 0 {
+			result = append(result, line)
+		}
+	}
+
+	if err = scanner.Err(); err != nil {
+		panic(fmt.Errorf("Cannot read makefile list: %s\n", err))
+	}
+	l.cachedMakefiles = result
 	return l.cachedMakefiles
 }
diff --git a/mk2rbc/expr.go b/mk2rbc/expr.go
index ec0b279..07f7ca1 100644
--- a/mk2rbc/expr.go
+++ b/mk2rbc/expr.go
@@ -16,21 +16,22 @@
 
 import (
 	"fmt"
-	"strconv"
 	"strings"
 )
 
-// Represents an expression in the Starlark code. An expression has
-// a type, and it can be evaluated.
+// Represents an expression in the Starlark code. An expression has a type.
 type starlarkExpr interface {
 	starlarkNode
 	typ() starlarkType
-	// Try to substitute variable values. Return substitution result
-	// and whether it is the same as the original expression.
-	eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool)
 	// Emit the code to copy the expression, otherwise we will end up
 	// with source and target pointing to the same list.
 	emitListVarCopy(gctx *generationContext)
+	// Return the expression, calling the transformer func for
+	// every expression in the tree. If the transformer func returns non-nil,
+	// its result is used in place of the expression it was called with in the
+	// resulting expression. The resulting starlarkExpr will contain as many
+	// of the same objects from the original expression as possible.
+	transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr
 }
 
 func maybeString(expr starlarkExpr) (string, bool) {
@@ -44,12 +45,6 @@
 	literal string
 }
 
-func (s *stringLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	res = s
-	same = true
-	return
-}
-
 func (s *stringLiteralExpr) emit(gctx *generationContext) {
 	gctx.writef("%q", s.literal)
 }
@@ -62,17 +57,19 @@
 	s.emit(gctx)
 }
 
+func (s *stringLiteralExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	if replacement := transformer(s); replacement != nil {
+		return replacement
+	} else {
+		return s
+	}
+}
+
 // Integer literal
 type intLiteralExpr struct {
 	literal int
 }
 
-func (s *intLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	res = s
-	same = true
-	return
-}
-
 func (s *intLiteralExpr) emit(gctx *generationContext) {
 	gctx.writef("%d", s.literal)
 }
@@ -85,6 +82,66 @@
 	s.emit(gctx)
 }
 
+func (s *intLiteralExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	if replacement := transformer(s); replacement != nil {
+		return replacement
+	} else {
+		return s
+	}
+}
+
+// Boolean literal
+type boolLiteralExpr struct {
+	literal bool
+}
+
+func (b *boolLiteralExpr) emit(gctx *generationContext) {
+	if b.literal {
+		gctx.write("True")
+	} else {
+		gctx.write("False")
+	}
+}
+
+func (_ *boolLiteralExpr) typ() starlarkType {
+	return starlarkTypeBool
+}
+
+func (b *boolLiteralExpr) emitListVarCopy(gctx *generationContext) {
+	b.emit(gctx)
+}
+
+func (b *boolLiteralExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	if replacement := transformer(b); replacement != nil {
+		return replacement
+	} else {
+		return b
+	}
+}
+
+type globalsExpr struct {
+}
+
+func (g *globalsExpr) emit(gctx *generationContext) {
+	gctx.write("g")
+}
+
+func (g *globalsExpr) typ() starlarkType {
+	return starlarkTypeUnknown
+}
+
+func (g *globalsExpr) emitListVarCopy(gctx *generationContext) {
+	g.emit(gctx)
+}
+
+func (g *globalsExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	if replacement := transformer(g); replacement != nil {
+		return replacement
+	} else {
+		return g
+	}
+}
+
 // interpolateExpr represents Starlark's interpolation operator <string> % list
 // we break <string> into a list of chunks, i.e., "first%second%third" % (X, Y)
 // will have chunks = ["first", "second", "third"] and args = [X, Y]
@@ -93,6 +150,35 @@
 	args   []starlarkExpr
 }
 
+func NewInterpolateExpr(parts []starlarkExpr) starlarkExpr {
+	result := &interpolateExpr{}
+	needString := true
+	for _, part := range parts {
+		if needString {
+			if strLit, ok := part.(*stringLiteralExpr); ok {
+				result.chunks = append(result.chunks, strLit.literal)
+			} else {
+				result.chunks = append(result.chunks, "")
+			}
+			needString = false
+		} else {
+			if strLit, ok := part.(*stringLiteralExpr); ok {
+				result.chunks[len(result.chunks)-1] += strLit.literal
+			} else {
+				result.args = append(result.args, part)
+				needString = true
+			}
+		}
+	}
+	if len(result.chunks) == len(result.args) {
+		result.chunks = append(result.chunks, "")
+	}
+	if len(result.args) == 0 {
+		return &stringLiteralExpr{literal: strings.Join(result.chunks, "")}
+	}
+	return result
+}
+
 func (xi *interpolateExpr) emit(gctx *generationContext) {
 	if len(xi.chunks) != len(xi.args)+1 {
 		panic(fmt.Errorf("malformed interpolateExpr: #chunks(%d) != #args(%d)+1",
@@ -126,37 +212,6 @@
 	}
 }
 
-func (xi *interpolateExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	same = true
-	newChunks := []string{xi.chunks[0]}
-	var newArgs []starlarkExpr
-	for i, arg := range xi.args {
-		newArg, sameArg := arg.eval(valueMap)
-		same = same && sameArg
-		switch x := newArg.(type) {
-		case *stringLiteralExpr:
-			newChunks[len(newChunks)-1] += x.literal + xi.chunks[i+1]
-			same = false
-			continue
-		case *intLiteralExpr:
-			newChunks[len(newChunks)-1] += strconv.Itoa(x.literal) + xi.chunks[i+1]
-			same = false
-			continue
-		default:
-			newChunks = append(newChunks, xi.chunks[i+1])
-			newArgs = append(newArgs, newArg)
-		}
-	}
-	if same {
-		res = xi
-	} else if len(newChunks) == 1 {
-		res = &stringLiteralExpr{newChunks[0]}
-	} else {
-		res = &interpolateExpr{chunks: newChunks, args: newArgs}
-	}
-	return
-}
-
 func (_ *interpolateExpr) typ() starlarkType {
 	return starlarkTypeString
 }
@@ -165,19 +220,29 @@
 	xi.emit(gctx)
 }
 
+func (xi *interpolateExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	argsCopy := make([]starlarkExpr, len(xi.args))
+	for i, arg := range xi.args {
+		argsCopy[i] = arg.transform(transformer)
+	}
+	xi.args = argsCopy
+	if replacement := transformer(xi); replacement != nil {
+		return replacement
+	} else {
+		return xi
+	}
+}
+
 type variableRefExpr struct {
 	ref       variable
 	isDefined bool
 }
 
-func (v *variableRefExpr) eval(map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	predefined, ok := v.ref.(*predefinedVariable)
-	if same = !ok; same {
-		res = v
-	} else {
-		res = predefined.value
+func NewVariableRefExpr(ref variable, isDefined bool) starlarkExpr {
+	if predefined, ok := ref.(*predefinedVariable); ok {
+		return predefined.value
 	}
-	return
+	return &variableRefExpr{ref, isDefined}
 }
 
 func (v *variableRefExpr) emit(gctx *generationContext) {
@@ -195,17 +260,16 @@
 	}
 }
 
-type toStringExpr struct {
-	expr starlarkExpr
+func (v *variableRefExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	if replacement := transformer(v); replacement != nil {
+		return replacement
+	} else {
+		return v
+	}
 }
 
-func (s *toStringExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	if x, same := s.expr.eval(valueMap); same {
-		res = s
-	} else {
-		res = &toStringExpr{expr: x}
-	}
-	return
+type toStringExpr struct {
+	expr starlarkExpr
 }
 
 func (s *toStringExpr) emit(ctx *generationContext) {
@@ -240,17 +304,17 @@
 	s.emit(gctx)
 }
 
-type notExpr struct {
-	expr starlarkExpr
+func (s *toStringExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	s.expr = s.expr.transform(transformer)
+	if replacement := transformer(s); replacement != nil {
+		return replacement
+	} else {
+		return s
+	}
 }
 
-func (n *notExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	if x, same := n.expr.eval(valueMap); same {
-		res = n
-	} else {
-		res = &notExpr{expr: x}
-	}
-	return
+type notExpr struct {
+	expr starlarkExpr
 }
 
 func (n *notExpr) emit(ctx *generationContext) {
@@ -266,52 +330,21 @@
 	n.emit(gctx)
 }
 
+func (n *notExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	n.expr = n.expr.transform(transformer)
+	if replacement := transformer(n); replacement != nil {
+		return replacement
+	} else {
+		return n
+	}
+}
+
 type eqExpr struct {
 	left, right starlarkExpr
 	isEq        bool // if false, it's !=
 }
 
-func (eq *eqExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	xLeft, sameLeft := eq.left.eval(valueMap)
-	xRight, sameRight := eq.right.eval(valueMap)
-	if same = sameLeft && sameRight; same {
-		res = eq
-	} else {
-		res = &eqExpr{left: xLeft, right: xRight, isEq: eq.isEq}
-	}
-	return
-}
-
 func (eq *eqExpr) emit(gctx *generationContext) {
-	var stringOperand string
-	var otherOperand starlarkExpr
-	if s, ok := maybeString(eq.left); ok {
-		stringOperand = s
-		otherOperand = eq.right
-	} else if s, ok := maybeString(eq.right); ok {
-		stringOperand = s
-		otherOperand = eq.left
-	}
-
-	// If we've identified one of the operands as being a string literal, check
-	// for some special cases we can do to simplify the resulting expression.
-	if otherOperand != nil {
-		if stringOperand == "" {
-			if eq.isEq {
-				gctx.write("not ")
-			}
-			otherOperand.emit(gctx)
-			return
-		}
-		if stringOperand == "true" && otherOperand.typ() == starlarkTypeBool {
-			if !eq.isEq {
-				gctx.write("not ")
-			}
-			otherOperand.emit(gctx)
-			return
-		}
-	}
-
 	if eq.left.typ() != eq.right.typ() {
 		eq.left = &toStringExpr{expr: eq.left}
 		eq.right = &toStringExpr{expr: eq.right}
@@ -335,54 +368,20 @@
 	eq.emit(gctx)
 }
 
-// variableDefinedExpr corresponds to Make's ifdef VAR
-type variableDefinedExpr struct {
-	v variable
-}
-
-func (v *variableDefinedExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	res = v
-	same = true
-	return
-
-}
-
-func (v *variableDefinedExpr) emit(gctx *generationContext) {
-	if v.v != nil {
-		v.v.emitDefined(gctx)
-		return
+func (eq *eqExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	eq.left = eq.left.transform(transformer)
+	eq.right = eq.right.transform(transformer)
+	if replacement := transformer(eq); replacement != nil {
+		return replacement
+	} else {
+		return eq
 	}
-	gctx.writef("%s(%q)", cfnWarning, "TODO(VAR)")
-}
-
-func (_ *variableDefinedExpr) typ() starlarkType {
-	return starlarkTypeBool
-}
-
-func (v *variableDefinedExpr) emitListVarCopy(gctx *generationContext) {
-	v.emit(gctx)
 }
 
 type listExpr struct {
 	items []starlarkExpr
 }
 
-func (l *listExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	newItems := make([]starlarkExpr, len(l.items))
-	same = true
-	for i, item := range l.items {
-		var sameItem bool
-		newItems[i], sameItem = item.eval(valueMap)
-		same = same && sameItem
-	}
-	if same {
-		res = l
-	} else {
-		res = &listExpr{newItems}
-	}
-	return
-}
-
 func (l *listExpr) emit(gctx *generationContext) {
 	if !gctx.inAssignment || len(l.items) < 2 {
 		gctx.write("[")
@@ -417,6 +416,19 @@
 	l.emit(gctx)
 }
 
+func (l *listExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	itemsCopy := make([]starlarkExpr, len(l.items))
+	for i, item := range l.items {
+		itemsCopy[i] = item.transform(transformer)
+	}
+	l.items = itemsCopy
+	if replacement := transformer(l); replacement != nil {
+		return replacement
+	} else {
+		return l
+	}
+}
+
 func newStringListExpr(items []string) *listExpr {
 	v := listExpr{}
 	for _, item := range items {
@@ -456,22 +468,6 @@
 	gctx.indentLevel -= 2
 }
 
-func (c *concatExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	same = true
-	xConcat := &concatExpr{items: make([]starlarkExpr, len(c.items))}
-	for i, item := range c.items {
-		var sameItem bool
-		xConcat.items[i], sameItem = item.eval(valueMap)
-		same = same && sameItem
-	}
-	if same {
-		res = c
-	} else {
-		res = xConcat
-	}
-	return
-}
-
 func (_ *concatExpr) typ() starlarkType {
 	return starlarkTypeList
 }
@@ -480,6 +476,19 @@
 	c.emit(gctx)
 }
 
+func (c *concatExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	itemsCopy := make([]starlarkExpr, len(c.items))
+	for i, item := range c.items {
+		itemsCopy[i] = item.transform(transformer)
+	}
+	c.items = itemsCopy
+	if replacement := transformer(c); replacement != nil {
+		return replacement
+	} else {
+		return c
+	}
+}
+
 // inExpr generates <expr> [not] in <list>
 type inExpr struct {
 	expr  starlarkExpr
@@ -487,19 +496,6 @@
 	isNot bool
 }
 
-func (i *inExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	x := &inExpr{isNot: i.isNot}
-	var sameExpr, sameList bool
-	x.expr, sameExpr = i.expr.eval(valueMap)
-	x.list, sameList = i.list.eval(valueMap)
-	if same = sameExpr && sameList; same {
-		res = i
-	} else {
-		res = x
-	}
-	return
-}
-
 func (i *inExpr) emit(gctx *generationContext) {
 	i.expr.emit(gctx)
 	if i.isNot {
@@ -518,35 +514,44 @@
 	i.emit(gctx)
 }
 
+func (i *inExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	i.expr = i.expr.transform(transformer)
+	i.list = i.list.transform(transformer)
+	if replacement := transformer(i); replacement != nil {
+		return replacement
+	} else {
+		return i
+	}
+}
+
 type indexExpr struct {
 	array starlarkExpr
 	index starlarkExpr
 }
 
-func (ix indexExpr) emit(gctx *generationContext) {
+func (ix *indexExpr) emit(gctx *generationContext) {
 	ix.array.emit(gctx)
 	gctx.write("[")
 	ix.index.emit(gctx)
 	gctx.write("]")
 }
 
-func (ix indexExpr) typ() starlarkType {
+func (ix *indexExpr) typ() starlarkType {
 	return starlarkTypeString
 }
 
-func (ix indexExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	newArray, isSameArray := ix.array.eval(valueMap)
-	newIndex, isSameIndex := ix.index.eval(valueMap)
-	if same = isSameArray && isSameIndex; same {
-		res = ix
-	} else {
-		res = &indexExpr{newArray, newIndex}
-	}
-	return
+func (ix *indexExpr) emitListVarCopy(gctx *generationContext) {
+	ix.emit(gctx)
 }
 
-func (ix indexExpr) emitListVarCopy(gctx *generationContext) {
-	ix.emit(gctx)
+func (ix *indexExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	ix.array = ix.array.transform(transformer)
+	ix.index = ix.index.transform(transformer)
+	if replacement := transformer(ix); replacement != nil {
+		return replacement
+	} else {
+		return ix
+	}
 }
 
 type callExpr struct {
@@ -556,51 +561,16 @@
 	returnType starlarkType
 }
 
-func (cx *callExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	newCallExpr := &callExpr{name: cx.name, args: make([]starlarkExpr, len(cx.args)),
-		returnType: cx.returnType}
-	if cx.object != nil {
-		newCallExpr.object, same = cx.object.eval(valueMap)
-	} else {
-		same = true
-	}
-	for i, args := range cx.args {
-		var s bool
-		newCallExpr.args[i], s = args.eval(valueMap)
-		same = same && s
-	}
-	if same {
-		res = cx
-	} else {
-		res = newCallExpr
-	}
-	return
-}
-
 func (cx *callExpr) emit(gctx *generationContext) {
-	sep := ""
 	if cx.object != nil {
 		gctx.write("(")
 		cx.object.emit(gctx)
 		gctx.write(")")
 		gctx.write(".", cx.name, "(")
 	} else {
-		kf, found := knownFunctions[cx.name]
-		if !found {
-			panic(fmt.Errorf("callExpr with unknown function %q", cx.name))
-		}
-		if kf.runtimeName[0] == '!' {
-			panic(fmt.Errorf("callExpr for %q should not be there", cx.name))
-		}
-		gctx.write(kf.runtimeName, "(")
-		if kf.hiddenArg == hiddenArgGlobal {
-			gctx.write("g")
-			sep = ", "
-		} else if kf.hiddenArg == hiddenArgConfig {
-			gctx.write("cfg")
-			sep = ", "
-		}
+		gctx.write(cx.name, "(")
 	}
+	sep := ""
 	for _, arg := range cx.args {
 		gctx.write(sep)
 		arg.emit(gctx)
@@ -617,17 +587,156 @@
 	cx.emit(gctx)
 }
 
+func (cx *callExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	if cx.object != nil {
+		cx.object = cx.object.transform(transformer)
+	}
+	argsCopy := make([]starlarkExpr, len(cx.args))
+	for i, arg := range cx.args {
+		argsCopy[i] = arg.transform(transformer)
+	}
+	if replacement := transformer(cx); replacement != nil {
+		return replacement
+	} else {
+		return cx
+	}
+}
+
+type ifExpr struct {
+	condition starlarkExpr
+	ifTrue    starlarkExpr
+	ifFalse   starlarkExpr
+}
+
+func (i *ifExpr) emit(gctx *generationContext) {
+	gctx.write("(")
+	i.ifTrue.emit(gctx)
+	gctx.write(" if ")
+	i.condition.emit(gctx)
+	gctx.write(" else ")
+	i.ifFalse.emit(gctx)
+	gctx.write(")")
+}
+
+func (i *ifExpr) typ() starlarkType {
+	tType := i.ifTrue.typ()
+	fType := i.ifFalse.typ()
+	if tType != fType && tType != starlarkTypeUnknown && fType != starlarkTypeUnknown {
+		panic("Conflicting types in if expression")
+	}
+	if tType != starlarkTypeUnknown {
+		return tType
+	} else {
+		return fType
+	}
+}
+
+func (i *ifExpr) emitListVarCopy(gctx *generationContext) {
+	i.emit(gctx)
+}
+
+func (i *ifExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	i.condition = i.condition.transform(transformer)
+	i.ifTrue = i.ifTrue.transform(transformer)
+	i.ifFalse = i.ifFalse.transform(transformer)
+	if replacement := transformer(i); replacement != nil {
+		return replacement
+	} else {
+		return i
+	}
+}
+
+type identifierExpr struct {
+	name string
+}
+
+func (i *identifierExpr) emit(gctx *generationContext) {
+	gctx.write(i.name)
+}
+
+func (i *identifierExpr) typ() starlarkType {
+	return starlarkTypeUnknown
+}
+
+func (i *identifierExpr) emitListVarCopy(gctx *generationContext) {
+	i.emit(gctx)
+}
+
+func (i *identifierExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	if replacement := transformer(i); replacement != nil {
+		return replacement
+	} else {
+		return i
+	}
+}
+
+type foreachExpr struct {
+	varName string
+	list    starlarkExpr
+	action  starlarkExpr
+}
+
+func (f *foreachExpr) emit(gctx *generationContext) {
+	gctx.write("[")
+	f.action.emit(gctx)
+	gctx.write(" for " + f.varName + " in ")
+	f.list.emit(gctx)
+	gctx.write("]")
+}
+
+func (f *foreachExpr) typ() starlarkType {
+	return starlarkTypeList
+}
+
+func (f *foreachExpr) emitListVarCopy(gctx *generationContext) {
+	f.emit(gctx)
+}
+
+func (f *foreachExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	f.list = f.list.transform(transformer)
+	f.action = f.action.transform(transformer)
+	if replacement := transformer(f); replacement != nil {
+		return replacement
+	} else {
+		return f
+	}
+}
+
+type binaryOpExpr struct {
+	left, right starlarkExpr
+	op          string
+	returnType  starlarkType
+}
+
+func (b *binaryOpExpr) emit(gctx *generationContext) {
+	b.left.emit(gctx)
+	gctx.write(" " + b.op + " ")
+	b.right.emit(gctx)
+}
+
+func (b *binaryOpExpr) typ() starlarkType {
+	return b.returnType
+}
+
+func (b *binaryOpExpr) emitListVarCopy(gctx *generationContext) {
+	b.emit(gctx)
+}
+
+func (b *binaryOpExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	b.left = b.left.transform(transformer)
+	b.right = b.right.transform(transformer)
+	if replacement := transformer(b); replacement != nil {
+		return replacement
+	} else {
+		return b
+	}
+}
+
 type badExpr struct {
 	errorLocation ErrorLocation
 	message       string
 }
 
-func (b *badExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
-	res = b
-	same = true
-	return
-}
-
 func (b *badExpr) emit(gctx *generationContext) {
 	gctx.emitConversionError(b.errorLocation, b.message)
 }
@@ -640,6 +749,14 @@
 	panic("implement me")
 }
 
+func (b *badExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	if replacement := transformer(b); replacement != nil {
+		return replacement
+	} else {
+		return b
+	}
+}
+
 func maybeConvertToStringList(expr starlarkExpr) starlarkExpr {
 	if xString, ok := expr.(*stringLiteralExpr); ok {
 		return newStringListExpr(strings.Fields(xString.literal))
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 4b9779c..cb50a50 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -54,7 +54,6 @@
 	cfnMain            = baseName + ".product_configuration"
 	cfnBoardMain       = baseName + ".board_configuration"
 	cfnPrintVars       = baseName + ".printvars"
-	cfnPrintGlobals    = baseName + ".printglobals"
 	cfnWarning         = baseName + ".warning"
 	cfnLocalAppend     = baseName + ".local_append"
 	cfnLocalSetDefault = baseName + ".local_set_default"
@@ -63,103 +62,86 @@
 )
 
 const (
-	// Phony makefile functions, they are eventually rewritten
-	// according to knownFunctions map
-	fileExistsPhony = "$file_exists"
-	// The following two macros are obsolete, and will we deleted once
-	// there are deleted from the makefiles:
-	soongConfigNamespaceOld = "add_soong_config_namespace"
-	soongConfigVarSetOld    = "add_soong_config_var_value"
-	soongConfigAppend       = "soong_config_append"
-	soongConfigAssign       = "soong_config_set"
-	soongConfigGet          = "soong_config_get"
-	wildcardExistsPhony     = "$wildcard_exists"
+	soongConfigAppend = "soong_config_append"
+	soongConfigAssign = "soong_config_set"
 )
 
-const (
-	callLoadAlways = "inherit-product"
-	callLoadIf     = "inherit-product-if-exists"
-)
-
-var knownFunctions = map[string]struct {
-	// The name of the runtime function this function call in makefiles maps to.
-	// If it starts with !, then this makefile function call is rewritten to
-	// something else.
-	runtimeName string
-	returnType  starlarkType
-	hiddenArg   hiddenArgType
+var knownFunctions = map[string]interface {
+	parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr
 }{
-	"abspath":                             {baseName + ".abspath", starlarkTypeString, hiddenArgNone},
-	fileExistsPhony:                       {baseName + ".file_exists", starlarkTypeBool, hiddenArgNone},
-	wildcardExistsPhony:                   {baseName + ".file_wildcard_exists", starlarkTypeBool, hiddenArgNone},
-	soongConfigNamespaceOld:               {baseName + ".soong_config_namespace", starlarkTypeVoid, hiddenArgGlobal},
-	soongConfigVarSetOld:                  {baseName + ".soong_config_set", starlarkTypeVoid, hiddenArgGlobal},
-	soongConfigAssign:                     {baseName + ".soong_config_set", starlarkTypeVoid, hiddenArgGlobal},
-	soongConfigAppend:                     {baseName + ".soong_config_append", starlarkTypeVoid, hiddenArgGlobal},
-	soongConfigGet:                        {baseName + ".soong_config_get", starlarkTypeString, hiddenArgGlobal},
-	"add-to-product-copy-files-if-exists": {baseName + ".copy_if_exists", starlarkTypeList, hiddenArgNone},
-	"addprefix":                           {baseName + ".addprefix", starlarkTypeList, hiddenArgNone},
-	"addsuffix":                           {baseName + ".addsuffix", starlarkTypeList, hiddenArgNone},
-	"copy-files":                          {baseName + ".copy_files", starlarkTypeList, hiddenArgNone},
-	"dir":                                 {baseName + ".dir", starlarkTypeList, hiddenArgNone},
-	"dist-for-goals":                      {baseName + ".mkdist_for_goals", starlarkTypeVoid, hiddenArgGlobal},
-	"enforce-product-packages-exist":      {baseName + ".enforce_product_packages_exist", starlarkTypeVoid, hiddenArgNone},
-	"error":                               {baseName + ".mkerror", starlarkTypeVoid, hiddenArgNone},
-	"findstring":                          {"!findstring", starlarkTypeInt, hiddenArgNone},
-	"find-copy-subdir-files":              {baseName + ".find_and_copy", starlarkTypeList, hiddenArgNone},
-	"find-word-in-list":                   {"!find-word-in-list", starlarkTypeUnknown, hiddenArgNone}, // internal macro
-	"filter":                              {baseName + ".filter", starlarkTypeList, hiddenArgNone},
-	"filter-out":                          {baseName + ".filter_out", starlarkTypeList, hiddenArgNone},
-	"firstword":                           {"!firstword", starlarkTypeString, hiddenArgNone},
-	"get-vendor-board-platforms":          {"!get-vendor-board-platforms", starlarkTypeList, hiddenArgNone}, // internal macro, used by is-board-platform, etc.
-	"info":                                {baseName + ".mkinfo", starlarkTypeVoid, hiddenArgNone},
-	"is-android-codename":                 {"!is-android-codename", starlarkTypeBool, hiddenArgNone},         // unused by product config
-	"is-android-codename-in-list":         {"!is-android-codename-in-list", starlarkTypeBool, hiddenArgNone}, // unused by product config
-	"is-board-platform":                   {"!is-board-platform", starlarkTypeBool, hiddenArgNone},
-	"is-board-platform2":                  {baseName + ".board_platform_is", starlarkTypeBool, hiddenArgGlobal},
-	"is-board-platform-in-list":           {"!is-board-platform-in-list", starlarkTypeBool, hiddenArgNone},
-	"is-board-platform-in-list2":          {baseName + ".board_platform_in", starlarkTypeBool, hiddenArgGlobal},
-	"is-chipset-in-board-platform":        {"!is-chipset-in-board-platform", starlarkTypeUnknown, hiddenArgNone},     // unused by product config
-	"is-chipset-prefix-in-board-platform": {"!is-chipset-prefix-in-board-platform", starlarkTypeBool, hiddenArgNone}, // unused by product config
-	"is-not-board-platform":               {"!is-not-board-platform", starlarkTypeBool, hiddenArgNone},               // defined but never used
-	"is-platform-sdk-version-at-least":    {"!is-platform-sdk-version-at-least", starlarkTypeBool, hiddenArgNone},    // unused by product config
-	"is-product-in-list":                  {"!is-product-in-list", starlarkTypeBool, hiddenArgNone},
-	"is-vendor-board-platform":            {"!is-vendor-board-platform", starlarkTypeBool, hiddenArgNone},
-	"is-vendor-board-qcom":                {"!is-vendor-board-qcom", starlarkTypeBool, hiddenArgNone},
-	callLoadAlways:                        {"!inherit-product", starlarkTypeVoid, hiddenArgNone},
-	callLoadIf:                            {"!inherit-product-if-exists", starlarkTypeVoid, hiddenArgNone},
-	"lastword":                            {"!lastword", starlarkTypeString, hiddenArgNone},
-	"match-prefix":                        {"!match-prefix", starlarkTypeUnknown, hiddenArgNone},       // internal macro
-	"match-word":                          {"!match-word", starlarkTypeUnknown, hiddenArgNone},         // internal macro
-	"match-word-in-list":                  {"!match-word-in-list", starlarkTypeUnknown, hiddenArgNone}, // internal macro
-	"notdir":                              {baseName + ".notdir", starlarkTypeString, hiddenArgNone},
-	"my-dir":                              {"!my-dir", starlarkTypeString, hiddenArgNone},
-	"patsubst":                            {baseName + ".mkpatsubst", starlarkTypeString, hiddenArgNone},
-	"product-copy-files-by-pattern":       {baseName + ".product_copy_files_by_pattern", starlarkTypeList, hiddenArgNone},
-	"require-artifacts-in-path":           {baseName + ".require_artifacts_in_path", starlarkTypeVoid, hiddenArgNone},
-	"require-artifacts-in-path-relaxed":   {baseName + ".require_artifacts_in_path_relaxed", starlarkTypeVoid, hiddenArgNone},
+	"abspath":                             &simpleCallParser{name: baseName + ".abspath", returnType: starlarkTypeString, addGlobals: false},
+	"add_soong_config_namespace":          &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true},
+	"add_soong_config_var_value":          &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+	soongConfigAssign:                     &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+	soongConfigAppend:                     &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true},
+	"soong_config_get":                    &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true},
+	"add-to-product-copy-files-if-exists": &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList, addGlobals: false},
+	"addprefix":                           &simpleCallParser{name: baseName + ".addprefix", returnType: starlarkTypeList, addGlobals: false},
+	"addsuffix":                           &simpleCallParser{name: baseName + ".addsuffix", returnType: starlarkTypeList, addGlobals: false},
+	"copy-files":                          &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList, addGlobals: false},
+	"dir":                                 &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeList, addGlobals: false},
+	"dist-for-goals":                      &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true},
+	"enforce-product-packages-exist":      &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid, addGlobals: false},
+	"error":                               &makeControlFuncParser{name: baseName + ".mkerror"},
+	"findstring":                          &simpleCallParser{name: baseName + ".findstring", returnType: starlarkTypeInt, addGlobals: false},
+	"find-copy-subdir-files":              &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList, addGlobals: false},
+	"filter":                              &simpleCallParser{name: baseName + ".filter", returnType: starlarkTypeList, addGlobals: false},
+	"filter-out":                          &simpleCallParser{name: baseName + ".filter_out", returnType: starlarkTypeList, addGlobals: false},
+	"firstword":                           &firstOrLastwordCallParser{isLastWord: false},
+	"foreach":                             &foreachCallPaser{},
+	"if":                                  &ifCallParser{},
+	"info":                                &makeControlFuncParser{name: baseName + ".mkinfo"},
+	"is-board-platform":                   &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
+	"is-board-platform2":                  &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
+	"is-board-platform-in-list":           &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
+	"is-board-platform-in-list2":          &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
+	"is-product-in-list":                  &isProductInListCallParser{},
+	"is-vendor-board-platform":            &isVendorBoardPlatformCallParser{},
+	"is-vendor-board-qcom":                &isVendorBoardQcomCallParser{},
+	"lastword":                            &firstOrLastwordCallParser{isLastWord: true},
+	"notdir":                              &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString, addGlobals: false},
+	"math_max":                            &mathMaxOrMinCallParser{function: "max"},
+	"math_min":                            &mathMaxOrMinCallParser{function: "min"},
+	"math_gt_or_eq":                       &mathComparisonCallParser{op: ">="},
+	"math_gt":                             &mathComparisonCallParser{op: ">"},
+	"math_lt":                             &mathComparisonCallParser{op: "<"},
+	"my-dir":                              &myDirCallParser{},
+	"patsubst":                            &substCallParser{fname: "patsubst"},
+	"product-copy-files-by-pattern":       &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList, addGlobals: false},
+	"require-artifacts-in-path":           &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid, addGlobals: false},
+	"require-artifacts-in-path-relaxed":   &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid, addGlobals: false},
 	// TODO(asmundak): remove it once all calls are removed from configuration makefiles. see b/183161002
-	"shell":      {baseName + ".shell", starlarkTypeString, hiddenArgNone},
-	"strip":      {baseName + ".mkstrip", starlarkTypeString, hiddenArgNone},
-	"tb-modules": {"!tb-modules", starlarkTypeUnknown, hiddenArgNone}, // defined in hardware/amlogic/tb_modules/tb_detect.mk, unused
-	"subst":      {baseName + ".mksubst", starlarkTypeString, hiddenArgNone},
-	"warning":    {baseName + ".mkwarning", starlarkTypeVoid, hiddenArgNone},
-	"word":       {baseName + "!word", starlarkTypeString, hiddenArgNone},
-	"wildcard":   {baseName + ".expand_wildcard", starlarkTypeList, hiddenArgNone},
+	"shell":    &shellCallParser{},
+	"strip":    &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString, addGlobals: false},
+	"subst":    &substCallParser{fname: "subst"},
+	"warning":  &makeControlFuncParser{name: baseName + ".mkwarning"},
+	"word":     &wordCallParser{},
+	"wildcard": &simpleCallParser{name: baseName + ".expand_wildcard", returnType: starlarkTypeList, addGlobals: false},
 }
 
-var builtinFuncRex = regexp.MustCompile(
-	"^(addprefix|addsuffix|abspath|and|basename|call|dir|error|eval" +
-		"|flavor|foreach|file|filter|filter-out|findstring|firstword|guile" +
-		"|if|info|join|lastword|notdir|or|origin|patsubst|realpath" +
-		"|shell|sort|strip|subst|suffix|value|warning|word|wordlist|words" +
-		"|wildcard)")
+// These are functions that we don't implement conversions for, but
+// we allow seeing their definitions in the product config files.
+var ignoredDefines = map[string]bool{
+	"find-word-in-list":                   true, // internal macro
+	"get-vendor-board-platforms":          true, // internal macro, used by is-board-platform, etc.
+	"is-android-codename":                 true, // unused by product config
+	"is-android-codename-in-list":         true, // unused by product config
+	"is-chipset-in-board-platform":        true, // unused by product config
+	"is-chipset-prefix-in-board-platform": true, // unused by product config
+	"is-not-board-platform":               true, // defined but never used
+	"is-platform-sdk-version-at-least":    true, // unused by product config
+	"match-prefix":                        true, // internal macro
+	"match-word":                          true, // internal macro
+	"match-word-in-list":                  true, // internal macro
+	"tb-modules":                          true, // defined in hardware/amlogic/tb_modules/tb_detect.mk, unused
+}
+
+var identifierFullMatchRegex = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
 
 // Conversion request parameters
 type Request struct {
 	MkFile          string    // file to convert
 	Reader          io.Reader // if set, read input from this stream instead
-	RootDir         string    // root directory path used to resolve included files
 	OutputSuffix    string    // generated Starlark files suffix
 	OutputDir       string    // if set, root of the output hierarchy
 	ErrorLogger     ErrorLogger
@@ -271,19 +253,19 @@
 	gctx.writef("load(%q, %q)", baseUri, baseName)
 	// Emit exactly one load statement for each URI.
 	loadedSubConfigs := make(map[string]string)
-	for _, sc := range gctx.starScript.inherited {
-		uri := sc.path
+	for _, mi := range gctx.starScript.inherited {
+		uri := mi.path
 		if m, ok := loadedSubConfigs[uri]; ok {
 			// No need to emit load statement, but fix module name.
-			sc.moduleLocalName = m
+			mi.moduleLocalName = m
 			continue
 		}
-		if sc.optional {
+		if mi.optional || mi.missing {
 			uri += "|init"
 		}
 		gctx.newLine()
-		gctx.writef("load(%q, %s = \"init\")", uri, sc.entryName())
-		loadedSubConfigs[uri] = sc.moduleLocalName
+		gctx.writef("load(%q, %s = \"init\")", uri, mi.entryName())
+		loadedSubConfigs[uri] = mi.moduleLocalName
 	}
 	gctx.write("\n")
 }
@@ -315,6 +297,20 @@
 	gctx.writef(`rblf.mk2rbc_error("%s", %q)`, el, message)
 }
 
+func (gctx *generationContext) emitLoadCheck(im inheritedModule) {
+	if !im.needsLoadCheck() {
+		return
+	}
+	gctx.newLine()
+	gctx.writef("if not %s:", im.entryName())
+	gctx.indentLevel++
+	gctx.newLine()
+	gctx.write(`rblf.mkerror("`, gctx.starScript.mkFile, `", "Cannot find %s" % (`)
+	im.pathExpr().emit(gctx)
+	gctx.write("))")
+	gctx.indentLevel--
+}
+
 type knownVariable struct {
 	name      string
 	class     varClass
@@ -373,10 +369,6 @@
 	}
 }
 
-type nodeReceiver interface {
-	newNode(node starlarkNode)
-}
-
 // Information about the generated Starlark script.
 type StarlarkScript struct {
 	mkFile         string
@@ -385,17 +377,12 @@
 	nodes          []starlarkNode
 	inherited      []*moduleInfo
 	hasErrors      bool
-	topDir         string
 	traceCalls     bool // print enter/exit each init function
 	sourceFS       fs.FS
 	makefileFinder MakefileFinder
 	nodeLocator    func(pos mkparser.Pos) int
 }
 
-func (ss *StarlarkScript) newNode(node starlarkNode) {
-	ss.nodes = append(ss.nodes, node)
-}
-
 // varAssignmentScope points to the last assignment for each variable
 // in the current block. It is used during the parsing to chain
 // the assignments to a variable together.
@@ -413,14 +400,11 @@
 	ifNestLevel      int
 	moduleNameCount  map[string]int // count of imported modules with given basename
 	fatalError       error
-	builtinMakeVars  map[string]starlarkExpr
 	outputSuffix     string
 	errorLogger      ErrorLogger
 	tracedVariables  map[string]bool // variables to be traced in the generated script
 	variables        map[string]variable
 	varAssignments   *varAssignmentScope
-	receiver         nodeReceiver // receptacle for the generated starlarkNode's
-	receiverStack    []nodeReceiver
 	outputDir        string
 	dependentModules map[string]*moduleInfo
 	soongNamespaces  map[string]map[string]bool
@@ -428,11 +412,10 @@
 }
 
 func newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext {
-	topdir, _ := filepath.Split(filepath.Join(ss.topDir, "foo"))
 	predefined := []struct{ name, value string }{
 		{"SRC_TARGET_DIR", filepath.Join("build", "make", "target")},
 		{"LOCAL_PATH", filepath.Dir(ss.mkFile)},
-		{"TOPDIR", topdir},
+		{"TOPDIR", ""}, // TOPDIR is just set to an empty string in cleanbuild.mk and core.mk
 		// TODO(asmundak): maybe read it from build/make/core/envsetup.mk?
 		{"TARGET_COPY_OUT_SYSTEM", "system"},
 		{"TARGET_COPY_OUT_SYSTEM_OTHER", "system_other"},
@@ -466,11 +449,10 @@
 		currentNodeIndex: 0,
 		ifNestLevel:      0,
 		moduleNameCount:  make(map[string]int),
-		builtinMakeVars:  map[string]starlarkExpr{},
 		variables:        make(map[string]variable),
 		dependentModules: make(map[string]*moduleInfo),
 		soongNamespaces:  make(map[string]map[string]bool),
-		includeTops:      []string{"vendor/google-devices"},
+		includeTops:      []string{},
 	}
 	ctx.pushVarAssignments()
 	for _, item := range predefined {
@@ -508,20 +490,6 @@
 	ctx.varAssignments = ctx.varAssignments.outer
 }
 
-func (ctx *parseContext) pushReceiver(rcv nodeReceiver) {
-	ctx.receiverStack = append(ctx.receiverStack, ctx.receiver)
-	ctx.receiver = rcv
-}
-
-func (ctx *parseContext) popReceiver() {
-	last := len(ctx.receiverStack) - 1
-	if last < 0 {
-		panic(fmt.Errorf("popReceiver: receiver stack empty"))
-	}
-	ctx.receiver = ctx.receiverStack[last]
-	ctx.receiverStack = ctx.receiverStack[0:last]
-}
-
 func (ctx *parseContext) hasNodes() bool {
 	return ctx.currentNodeIndex < len(ctx.nodes)
 }
@@ -542,11 +510,10 @@
 	ctx.currentNodeIndex--
 }
 
-func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) {
+func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) []starlarkNode {
 	// Handle only simple variables
 	if !a.Name.Const() {
-		ctx.errorf(a, "Only simple variables are handled")
-		return
+		return []starlarkNode{ctx.newBadNode(a, "Only simple variables are handled")}
 	}
 	name := a.Name.Strings[0]
 	// The `override` directive
@@ -554,18 +521,16 @@
 	// is parsed as an assignment to a variable named `override FOO`.
 	// There are very few places where `override` is used, just flag it.
 	if strings.HasPrefix(name, "override ") {
-		ctx.errorf(a, "cannot handle override directive")
+		return []starlarkNode{ctx.newBadNode(a, "cannot handle override directive")}
 	}
 
 	// Soong configuration
 	if strings.HasPrefix(name, soongNsPrefix) {
-		ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a)
-		return
+		return ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a)
 	}
 	lhs := ctx.addVariable(name)
 	if lhs == nil {
-		ctx.errorf(a, "unknown variable %s", name)
-		return
+		return []starlarkNode{ctx.newBadNode(a, "unknown variable %s", name)}
 	}
 	_, isTraced := ctx.tracedVariables[name]
 	asgn := &assignmentNode{lhs: lhs, mkValue: a.Value, isTraced: isTraced, location: ctx.errorLocation(a)}
@@ -573,8 +538,7 @@
 		// Try to divine variable type from the RHS
 		asgn.value = ctx.parseMakeString(a, a.Value)
 		if xBad, ok := asgn.value.(*badExpr); ok {
-			ctx.wrapBadExpr(xBad)
-			return
+			return []starlarkNode{&exprNode{xBad}}
 		}
 		inferred_type := asgn.value.typ()
 		if inferred_type != starlarkTypeUnknown {
@@ -582,9 +546,9 @@
 		}
 	}
 	if lhs.valueType() == starlarkTypeList {
-		xConcat := ctx.buildConcatExpr(a)
-		if xConcat == nil {
-			return
+		xConcat, xBad := ctx.buildConcatExpr(a)
+		if xBad != nil {
+			return []starlarkNode{&exprNode{expr: xBad}}
 		}
 		switch len(xConcat.items) {
 		case 0:
@@ -597,14 +561,10 @@
 	} else {
 		asgn.value = ctx.parseMakeString(a, a.Value)
 		if xBad, ok := asgn.value.(*badExpr); ok {
-			ctx.wrapBadExpr(xBad)
-			return
+			return []starlarkNode{&exprNode{expr: xBad}}
 		}
 	}
 
-	// TODO(asmundak): move evaluation to a separate pass
-	asgn.value, _ = asgn.value.eval(ctx.builtinMakeVars)
-
 	asgn.previous = ctx.lastAssignment(name)
 	ctx.setLastAssignment(name, asgn)
 	switch a.Type {
@@ -622,16 +582,14 @@
 		panic(fmt.Errorf("unexpected assignment type %s", a.Type))
 	}
 
-	ctx.receiver.newNode(asgn)
+	return []starlarkNode{asgn}
 }
 
-func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Assignment) {
+func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Assignment) []starlarkNode {
 	val := ctx.parseMakeString(asgn, asgn.Value)
 	if xBad, ok := val.(*badExpr); ok {
-		ctx.wrapBadExpr(xBad)
-		return
+		return []starlarkNode{&exprNode{expr: xBad}}
 	}
-	val, _ = val.eval(ctx.builtinMakeVars)
 
 	// Unfortunately, Soong namespaces can be set up by directly setting corresponding Make
 	// variables instead of via add_soong_config_namespace + add_soong_config_var_value.
@@ -643,17 +601,18 @@
 		//      $(call add_soong_config_namespace,foo)
 		s, ok := maybeString(val)
 		if !ok {
-			ctx.errorf(asgn, "cannot handle variables in SOONG_CONFIG_NAMESPACES assignment, please use add_soong_config_namespace instead")
-			return
+			return []starlarkNode{ctx.newBadNode(asgn, "cannot handle variables in SOONG_CONFIG_NAMESPACES assignment, please use add_soong_config_namespace instead")}
 		}
+		result := make([]starlarkNode, 0)
 		for _, ns := range strings.Fields(s) {
 			ctx.addSoongNamespace(ns)
-			ctx.receiver.newNode(&exprNode{&callExpr{
-				name:       soongConfigNamespaceOld,
-				args:       []starlarkExpr{&stringLiteralExpr{ns}},
+			result = append(result, &exprNode{&callExpr{
+				name:       baseName + ".soong_config_namespace",
+				args:       []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{ns}},
 				returnType: starlarkTypeVoid,
 			}})
 		}
+		return result
 	} else {
 		// Upon seeing
 		//      SOONG_CONFIG_x_y = v
@@ -673,45 +632,41 @@
 				continue
 			}
 			if namespaceName != "" {
-				ctx.errorf(asgn, "ambiguous soong namespace (may be either `%s` or  `%s`)", namespaceName, name[0:pos])
-				return
+				return []starlarkNode{ctx.newBadNode(asgn, "ambiguous soong namespace (may be either `%s` or  `%s`)", namespaceName, name[0:pos])}
 			}
 			namespaceName = name[0:pos]
 			varName = name[pos+1:]
 		}
 		if namespaceName == "" {
-			ctx.errorf(asgn, "cannot figure out Soong namespace, please use add_soong_config_var_value macro instead")
-			return
+			return []starlarkNode{ctx.newBadNode(asgn, "cannot figure out Soong namespace, please use add_soong_config_var_value macro instead")}
 		}
 		if varName == "" {
 			// Remember variables in this namespace
 			s, ok := maybeString(val)
 			if !ok {
-				ctx.errorf(asgn, "cannot handle variables in SOONG_CONFIG_ assignment, please use add_soong_config_var_value instead")
-				return
+				return []starlarkNode{ctx.newBadNode(asgn, "cannot handle variables in SOONG_CONFIG_ assignment, please use add_soong_config_var_value instead")}
 			}
 			ctx.updateSoongNamespace(asgn.Type != "+=", namespaceName, strings.Fields(s))
-			return
+			return []starlarkNode{}
 		}
 
 		// Finally, handle assignment to a namespace variable
 		if !ctx.hasNamespaceVar(namespaceName, varName) {
-			ctx.errorf(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)
-			return
+			return []starlarkNode{ctx.newBadNode(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)}
 		}
-		fname := soongConfigAssign
+		fname := baseName + "." + soongConfigAssign
 		if asgn.Type == "+=" {
-			fname = soongConfigAppend
+			fname = baseName + "." + soongConfigAppend
 		}
-		ctx.receiver.newNode(&exprNode{&callExpr{
+		return []starlarkNode{&exprNode{&callExpr{
 			name:       fname,
-			args:       []starlarkExpr{&stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val},
+			args:       []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val},
 			returnType: starlarkTypeVoid,
-		}})
+		}}}
 	}
 }
 
-func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) *concatExpr {
+func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) (*concatExpr, *badExpr) {
 	xConcat := &concatExpr{}
 	var xItemList *listExpr
 	addToItemList := func(x ...starlarkExpr) {
@@ -733,8 +688,7 @@
 		// expressions return individual elements.
 		switch x := ctx.parseMakeString(a, item).(type) {
 		case *badExpr:
-			ctx.wrapBadExpr(x)
-			return nil
+			return nil, x
 		case *stringLiteralExpr:
 			addToItemList(maybeConvertToStringList(x).(*listExpr).items...)
 		default:
@@ -758,7 +712,7 @@
 	if xItemList != nil {
 		xConcat.items = append(xConcat.items, xItemList)
 	}
-	return xConcat
+	return xConcat, nil
 }
 
 func (ctx *parseContext) newDependentModule(path string, optional bool) *moduleInfo {
@@ -774,11 +728,13 @@
 		moduleLocalName += fmt.Sprintf("%d", n)
 	}
 	ctx.moduleNameCount[moduleName] = n + 1
+	_, err := fs.Stat(ctx.script.sourceFS, path)
 	mi := &moduleInfo{
 		path:            modulePath,
 		originalPath:    path,
 		moduleLocalName: moduleLocalName,
 		optional:        optional,
+		missing:         err != nil,
 	}
 	ctx.dependentModules[modulePath] = mi
 	ctx.script.inherited = append(ctx.script.inherited, mi)
@@ -786,8 +742,7 @@
 }
 
 func (ctx *parseContext) handleSubConfig(
-	v mkparser.Node, pathExpr starlarkExpr, loadAlways bool, processModule func(inheritedModule)) {
-	pathExpr, _ = pathExpr.eval(ctx.builtinMakeVars)
+	v mkparser.Node, pathExpr starlarkExpr, loadAlways bool, processModule func(inheritedModule) starlarkNode) []starlarkNode {
 
 	// In a simple case, the name of a module to inherit/include is known statically.
 	if path, ok := maybeString(pathExpr); ok {
@@ -796,18 +751,19 @@
 		moduleShouldExist := loadAlways && ctx.ifNestLevel == 0
 		if strings.Contains(path, "*") {
 			if paths, err := fs.Glob(ctx.script.sourceFS, path); err == nil {
+				result := make([]starlarkNode, 0)
 				for _, p := range paths {
 					mi := ctx.newDependentModule(p, !moduleShouldExist)
-					processModule(inheritedStaticModule{mi, loadAlways})
+					result = append(result, processModule(inheritedStaticModule{mi, loadAlways}))
 				}
+				return result
 			} else {
-				ctx.errorf(v, "cannot glob wildcard argument")
+				return []starlarkNode{ctx.newBadNode(v, "cannot glob wildcard argument")}
 			}
 		} else {
 			mi := ctx.newDependentModule(path, !moduleShouldExist)
-			processModule(inheritedStaticModule{mi, loadAlways})
+			return []starlarkNode{processModule(inheritedStaticModule{mi, loadAlways})}
 		}
-		return
 	}
 
 	// If module path references variables (e.g., $(v1)/foo/$(v2)/device-config.mk), find all the paths in the
@@ -827,8 +783,7 @@
 	var matchingPaths []string
 	varPath, ok := pathExpr.(*interpolateExpr)
 	if !ok {
-		ctx.errorf(v, "inherit-product/include argument is too complex")
-		return
+		return []starlarkNode{ctx.newBadNode(v, "inherit-product/include argument is too complex")}
 	}
 
 	pathPattern := []string{varPath.chunks[0]}
@@ -837,7 +792,7 @@
 			pathPattern = append(pathPattern, chunk)
 		}
 	}
-	if pathPattern[0] == "" {
+	if pathPattern[0] == "" && len(ctx.includeTops) > 0 {
 		// If pattern starts from the top. restrict it to the directories where
 		// we know inherit-product uses dynamically calculated path.
 		for _, p := range ctx.includeTops {
@@ -850,21 +805,26 @@
 	// Safeguard against $(call inherit-product,$(PRODUCT_PATH))
 	const maxMatchingFiles = 150
 	if len(matchingPaths) > maxMatchingFiles {
-		ctx.errorf(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)
-		return
+		return []starlarkNode{ctx.newBadNode(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)}
 	}
-	res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways}
-	for _, p := range matchingPaths {
-		// A product configuration files discovered dynamically may attempt to inherit
-		// from another one which does not exist in this source tree. Prevent load errors
-		// by always loading the dynamic files as optional.
-		res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
+	if len(matchingPaths) == 1 {
+		res := inheritedStaticModule{ctx.newDependentModule(matchingPaths[0], loadAlways && ctx.ifNestLevel == 0), loadAlways}
+		return []starlarkNode{processModule(res)}
+	} else {
+		needsWarning := pathPattern[0] == "" && len(ctx.includeTops) == 0
+		res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning}
+		for _, p := range matchingPaths {
+			// A product configuration files discovered dynamically may attempt to inherit
+			// from another one which does not exist in this source tree. Prevent load errors
+			// by always loading the dynamic files as optional.
+			res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
+		}
+		return []starlarkNode{processModule(res)}
 	}
-	processModule(res)
 }
 
 func (ctx *parseContext) findMatchingPaths(pattern []string) []string {
-	files := ctx.script.makefileFinder.Find(ctx.script.topDir)
+	files := ctx.script.makefileFinder.Find(".")
 	if len(pattern) == 0 {
 		return files
 	}
@@ -887,90 +847,90 @@
 	return res
 }
 
-func (ctx *parseContext) handleInheritModule(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) {
-	ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) {
-		ctx.receiver.newNode(&inheritNode{im, loadAlways})
+func (ctx *parseContext) handleInheritModule(v mkparser.Node, args *mkparser.MakeString, loadAlways bool) []starlarkNode {
+	args.TrimLeftSpaces()
+	args.TrimRightSpaces()
+	pathExpr := ctx.parseMakeString(v, args)
+	if _, ok := pathExpr.(*badExpr); ok {
+		return []starlarkNode{ctx.newBadNode(v, "Unable to parse argument to inherit")}
+	}
+	return ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) starlarkNode {
+		return &inheritNode{im, loadAlways}
 	})
 }
 
-func (ctx *parseContext) handleInclude(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) {
-	ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) {
-		ctx.receiver.newNode(&includeNode{im, loadAlways})
+func (ctx *parseContext) handleInclude(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) []starlarkNode {
+	return ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) starlarkNode {
+		return &includeNode{im, loadAlways}
 	})
 }
 
-func (ctx *parseContext) handleVariable(v *mkparser.Variable) {
+func (ctx *parseContext) handleVariable(v *mkparser.Variable) []starlarkNode {
 	// Handle:
 	//   $(call inherit-product,...)
 	//   $(call inherit-product-if-exists,...)
 	//   $(info xxx)
 	//   $(warning xxx)
 	//   $(error xxx)
-	expr := ctx.parseReference(v, v.Name)
-	switch x := expr.(type) {
-	case *callExpr:
-		if x.name == callLoadAlways || x.name == callLoadIf {
-			ctx.handleInheritModule(v, x.args[0], x.name == callLoadAlways)
-		} else if isMakeControlFunc(x.name) {
-			// File name is the first argument
-			args := []starlarkExpr{
-				&stringLiteralExpr{ctx.script.mkFile},
-				x.args[0],
-			}
-			ctx.receiver.newNode(&exprNode{
-				&callExpr{name: x.name, args: args, returnType: starlarkTypeUnknown},
-			})
-		} else {
-			ctx.receiver.newNode(&exprNode{expr})
-		}
-	case *badExpr:
-		ctx.wrapBadExpr(x)
-		return
-	default:
-		ctx.errorf(v, "cannot handle %s", v.Dump())
-		return
+	//   $(call other-custom-functions,...)
+
+	// inherit-product(-if-exists) gets converted to a series of statements,
+	// not just a single expression like parseReference returns. So handle it
+	// separately at the beginning here.
+	if strings.HasPrefix(v.Name.Dump(), "call inherit-product,") {
+		args := v.Name.Clone()
+		args.ReplaceLiteral("call inherit-product,", "")
+		return ctx.handleInheritModule(v, args, true)
 	}
+	if strings.HasPrefix(v.Name.Dump(), "call inherit-product-if-exists,") {
+		args := v.Name.Clone()
+		args.ReplaceLiteral("call inherit-product-if-exists,", "")
+		return ctx.handleInheritModule(v, args, false)
+	}
+	return []starlarkNode{&exprNode{expr: ctx.parseReference(v, v.Name)}}
 }
 
-func (ctx *parseContext) handleDefine(directive *mkparser.Directive) {
+func (ctx *parseContext) maybeHandleDefine(directive *mkparser.Directive) starlarkNode {
 	macro_name := strings.Fields(directive.Args.Strings[0])[0]
 	// Ignore the macros that we handle
-	if _, ok := knownFunctions[macro_name]; !ok {
-		ctx.errorf(directive, "define is not supported: %s", macro_name)
+	_, ignored := ignoredDefines[macro_name]
+	_, known := knownFunctions[macro_name]
+	if !ignored && !known {
+		return ctx.newBadNode(directive, "define is not supported: %s", macro_name)
 	}
+	return nil
 }
 
-func (ctx *parseContext) handleIfBlock(ifDirective *mkparser.Directive) {
-	ssSwitch := &switchNode{}
-	ctx.pushReceiver(ssSwitch)
-	for ctx.processBranch(ifDirective); ctx.hasNodes() && ctx.fatalError == nil; {
+func (ctx *parseContext) handleIfBlock(ifDirective *mkparser.Directive) starlarkNode {
+	ssSwitch := &switchNode{
+		ssCases: []*switchCase{ctx.processBranch(ifDirective)},
+	}
+	for ctx.hasNodes() && ctx.fatalError == nil {
 		node := ctx.getNode()
 		switch x := node.(type) {
 		case *mkparser.Directive:
 			switch x.Name {
 			case "else", "elifdef", "elifndef", "elifeq", "elifneq":
-				ctx.processBranch(x)
+				ssSwitch.ssCases = append(ssSwitch.ssCases, ctx.processBranch(x))
 			case "endif":
-				ctx.popReceiver()
-				ctx.receiver.newNode(ssSwitch)
-				return
+				return ssSwitch
 			default:
-				ctx.errorf(node, "unexpected directive %s", x.Name)
+				return ctx.newBadNode(node, "unexpected directive %s", x.Name)
 			}
 		default:
-			ctx.errorf(ifDirective, "unexpected statement")
+			return ctx.newBadNode(ifDirective, "unexpected statement")
 		}
 	}
 	if ctx.fatalError == nil {
 		ctx.fatalError = fmt.Errorf("no matching endif for %s", ifDirective.Dump())
 	}
-	ctx.popReceiver()
+	return ctx.newBadNode(ifDirective, "no matching endif for %s", ifDirective.Dump())
 }
 
 // processBranch processes a single branch (if/elseif/else) until the next directive
 // on the same level.
-func (ctx *parseContext) processBranch(check *mkparser.Directive) {
-	block := switchCase{gate: ctx.parseCondition(check)}
+func (ctx *parseContext) processBranch(check *mkparser.Directive) *switchCase {
+	block := &switchCase{gate: ctx.parseCondition(check)}
 	defer func() {
 		ctx.popVarAssignments()
 		ctx.ifNestLevel--
@@ -979,37 +939,29 @@
 	ctx.pushVarAssignments()
 	ctx.ifNestLevel++
 
-	ctx.pushReceiver(&block)
 	for ctx.hasNodes() {
 		node := ctx.getNode()
 		if d, ok := node.(*mkparser.Directive); ok {
 			switch d.Name {
 			case "else", "elifdef", "elifndef", "elifeq", "elifneq", "endif":
-				ctx.popReceiver()
-				ctx.receiver.newNode(&block)
 				ctx.backNode()
-				return
+				return block
 			}
 		}
-		ctx.handleSimpleStatement(node)
+		block.nodes = append(block.nodes, ctx.handleSimpleStatement(node)...)
 	}
 	ctx.fatalError = fmt.Errorf("no matching endif for %s", check.Dump())
-	ctx.popReceiver()
-}
-
-func (ctx *parseContext) newIfDefinedNode(check *mkparser.Directive) (starlarkExpr, bool) {
-	if !check.Args.Const() {
-		return ctx.newBadExpr(check, "ifdef variable ref too complex: %s", check.Args.Dump()), false
-	}
-	v := ctx.addVariable(check.Args.Strings[0])
-	return &variableDefinedExpr{v}, true
+	return block
 }
 
 func (ctx *parseContext) parseCondition(check *mkparser.Directive) starlarkNode {
 	switch check.Name {
 	case "ifdef", "ifndef", "elifdef", "elifndef":
-		v, ok := ctx.newIfDefinedNode(check)
-		if ok && strings.HasSuffix(check.Name, "ndef") {
+		if !check.Args.Const() {
+			return ctx.newBadNode(check, "ifdef variable ref too complex: %s", check.Args.Dump())
+		}
+		v := NewVariableRefExpr(ctx.addVariable(check.Args.Strings[0]), false)
+		if strings.HasSuffix(check.Name, "ndef") {
 			v = &notExpr{v}
 		}
 		return &ifNode{
@@ -1029,12 +981,16 @@
 }
 
 func (ctx *parseContext) newBadExpr(node mkparser.Node, text string, args ...interface{}) starlarkExpr {
-	message := fmt.Sprintf(text, args...)
 	if ctx.errorLogger != nil {
 		ctx.errorLogger.NewError(ctx.errorLocation(node), node, text, args...)
 	}
 	ctx.script.hasErrors = true
-	return &badExpr{errorLocation: ctx.errorLocation(node), message: message}
+	return &badExpr{errorLocation: ctx.errorLocation(node), message: fmt.Sprintf(text, args...)}
+}
+
+// records that the given node failed to be converted and includes an explanatory message
+func (ctx *parseContext) newBadNode(failedNode mkparser.Node, message string, args ...interface{}) starlarkNode {
+	return &exprNode{ctx.newBadExpr(failedNode, message, args...)}
 }
 
 func (ctx *parseContext) parseCompare(cond *mkparser.Directive) starlarkExpr {
@@ -1065,12 +1021,78 @@
 		return expr
 	}
 
+	var stringOperand string
+	var otherOperand starlarkExpr
+	if s, ok := maybeString(xLeft); ok {
+		stringOperand = s
+		otherOperand = xRight
+	} else if s, ok := maybeString(xRight); ok {
+		stringOperand = s
+		otherOperand = xLeft
+	}
+
+	not := func(expr starlarkExpr) starlarkExpr {
+		switch typedExpr := expr.(type) {
+		case *inExpr:
+			typedExpr.isNot = !typedExpr.isNot
+			return typedExpr
+		case *eqExpr:
+			typedExpr.isEq = !typedExpr.isEq
+			return typedExpr
+		case *binaryOpExpr:
+			switch typedExpr.op {
+			case ">":
+				typedExpr.op = "<="
+				return typedExpr
+			case "<":
+				typedExpr.op = ">="
+				return typedExpr
+			case ">=":
+				typedExpr.op = "<"
+				return typedExpr
+			case "<=":
+				typedExpr.op = ">"
+				return typedExpr
+			default:
+				return &notExpr{expr: expr}
+			}
+		default:
+			return &notExpr{expr: expr}
+		}
+	}
+
+	// If we've identified one of the operands as being a string literal, check
+	// for some special cases we can do to simplify the resulting expression.
+	if otherOperand != nil {
+		if stringOperand == "" {
+			if isEq {
+				return not(otherOperand)
+			} else {
+				return otherOperand
+			}
+		}
+		if stringOperand == "true" && otherOperand.typ() == starlarkTypeBool {
+			if !isEq {
+				return not(otherOperand)
+			} else {
+				return otherOperand
+			}
+		}
+		if intOperand, err := strconv.Atoi(strings.TrimSpace(stringOperand)); err == nil && otherOperand.typ() == starlarkTypeInt {
+			return &eqExpr{
+				left:  otherOperand,
+				right: &intLiteralExpr{literal: intOperand},
+				isEq:  isEq,
+			}
+		}
+	}
+
 	return &eqExpr{left: xLeft, right: xRight, isEq: isEq}
 }
 
 // Given an if statement's directive and the left/right starlarkExprs,
 // check if the starlarkExprs are one of a few hardcoded special cases
-// that can be converted to a simpler equalify expression than simply comparing
+// that can be converted to a simpler equality expression than simply comparing
 // the two.
 func (ctx *parseContext) parseCompareSpecialCases(directive *mkparser.Directive, left starlarkExpr,
 	right starlarkExpr) (starlarkExpr, bool) {
@@ -1098,166 +1120,53 @@
 		return nil, false
 	}
 
-	checkIsSomethingFunction := func(xCall *callExpr) starlarkExpr {
-		s, ok := maybeString(value)
-		if !ok || s != "true" {
-			return ctx.newBadExpr(directive,
-				fmt.Sprintf("the result of %s can be compared only to 'true'", xCall.name))
-		}
-		if len(xCall.args) < 1 {
-			return ctx.newBadExpr(directive, "%s requires an argument", xCall.name)
-		}
-		return nil
-	}
-
 	switch call.name {
-	case "filter":
-		return ctx.parseCompareFilterFuncResult(directive, call, value, isEq), true
-	case "filter-out":
-		return ctx.parseCompareFilterFuncResult(directive, call, value, !isEq), true
-	case "wildcard":
+	case baseName + ".filter":
+		return ctx.parseCompareFilterFuncResult(directive, call, value, isEq)
+	case baseName + ".expand_wildcard":
 		return ctx.parseCompareWildcardFuncResult(directive, call, value, !isEq), true
-	case "findstring":
+	case baseName + ".findstring":
 		return ctx.parseCheckFindstringFuncResult(directive, call, value, !isEq), true
-	case "strip":
+	case baseName + ".strip":
 		return ctx.parseCompareStripFuncResult(directive, call, value, !isEq), true
-	case "is-board-platform":
-		if xBad := checkIsSomethingFunction(call); xBad != nil {
-			return xBad, true
-		}
-		return &eqExpr{
-			left:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
-			right: call.args[0],
-			isEq:  isEq,
-		}, true
-	case "is-board-platform-in-list":
-		if xBad := checkIsSomethingFunction(call); xBad != nil {
-			return xBad, true
-		}
-		return &inExpr{
-			expr:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
-			list:  maybeConvertToStringList(call.args[0]),
-			isNot: !isEq,
-		}, true
-	case "is-product-in-list":
-		if xBad := checkIsSomethingFunction(call); xBad != nil {
-			return xBad, true
-		}
-		return &inExpr{
-			expr:  &variableRefExpr{ctx.addVariable("TARGET_PRODUCT"), true},
-			list:  maybeConvertToStringList(call.args[0]),
-			isNot: !isEq,
-		}, true
-	case "is-vendor-board-platform":
-		if xBad := checkIsSomethingFunction(call); xBad != nil {
-			return xBad, true
-		}
-		s, ok := maybeString(call.args[0])
-		if !ok {
-			return ctx.newBadExpr(directive, "cannot handle non-constant argument to is-vendor-board-platform"), true
-		}
-		return &inExpr{
-			expr:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
-			list:  &variableRefExpr{ctx.addVariable(s + "_BOARD_PLATFORMS"), true},
-			isNot: !isEq,
-		}, true
-
-	case "is-board-platform2", "is-board-platform-in-list2":
-		if s, ok := maybeString(value); !ok || s != "" {
-			return ctx.newBadExpr(directive,
-				fmt.Sprintf("the result of %s can be compared only to empty", call.name)), true
-		}
-		if len(call.args) != 1 {
-			return ctx.newBadExpr(directive, "%s requires an argument", call.name), true
-		}
-		cc := &callExpr{
-			name:       call.name,
-			args:       []starlarkExpr{call.args[0]},
-			returnType: starlarkTypeBool,
-		}
-		if isEq {
-			return &notExpr{cc}, true
-		}
-		return cc, true
-	case "is-vendor-board-qcom":
-		if s, ok := maybeString(value); !ok || s != "" {
-			return ctx.newBadExpr(directive,
-				fmt.Sprintf("the result of %s can be compared only to empty", call.name)), true
-		}
-		// if the expression is ifneq (,$(call is-vendor-board-platform,...)), negate==true,
-		// so we should set inExpr.isNot to false
-		return &inExpr{
-			expr:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
-			list:  &variableRefExpr{ctx.addVariable("QCOM_BOARD_PLATFORMS"), true},
-			isNot: isEq,
-		}, true
 	}
 	return nil, false
 }
 
 func (ctx *parseContext) parseCompareFilterFuncResult(cond *mkparser.Directive,
-	filterFuncCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
+	filterFuncCall *callExpr, xValue starlarkExpr, negate bool) (starlarkExpr, bool) {
 	// We handle:
 	// *  ifeq/ifneq (,$(filter v1 v2 ..., EXPR) becomes if EXPR not in/in ["v1", "v2", ...]
 	// *  ifeq/ifneq (,$(filter EXPR, v1 v2 ...) becomes if EXPR not in/in ["v1", "v2", ...]
-	// *  ifeq/ifneq ($(VAR),$(filter $(VAR), v1 v2 ...) becomes if VAR in/not in ["v1", "v2"]
-	// TODO(Asmundak): check the last case works for filter-out, too.
+	if x, ok := xValue.(*stringLiteralExpr); !ok || x.literal != "" {
+		return nil, false
+	}
 	xPattern := filterFuncCall.args[0]
 	xText := filterFuncCall.args[1]
 	var xInList *stringLiteralExpr
 	var expr starlarkExpr
 	var ok bool
-	switch x := xValue.(type) {
-	case *stringLiteralExpr:
-		if x.literal != "" {
-			return ctx.newBadExpr(cond, "filter comparison to non-empty value: %s", xValue)
-		}
-		// Either pattern or text should be const, and the
-		// non-const one should be varRefExpr
-		if xInList, ok = xPattern.(*stringLiteralExpr); ok && !strings.ContainsRune(xInList.literal, '%') && xText.typ() == starlarkTypeList {
-			expr = xText
-		} else if xInList, ok = xText.(*stringLiteralExpr); ok {
-			expr = xPattern
-		} else {
-			expr = &callExpr{
-				object:     nil,
-				name:       filterFuncCall.name,
-				args:       filterFuncCall.args,
-				returnType: starlarkTypeBool,
-			}
-			if negate {
-				expr = &notExpr{expr: expr}
-			}
-			return expr
-		}
-	case *variableRefExpr:
-		if v, ok := xPattern.(*variableRefExpr); ok {
-			if xInList, ok = xText.(*stringLiteralExpr); ok && v.ref.name() == x.ref.name() {
-				// ifeq/ifneq ($(VAR),$(filter $(VAR), v1 v2 ...), flip negate,
-				// it's the opposite to what is done when comparing to empty.
-				expr = xPattern
-				negate = !negate
-			}
-		}
+	if xInList, ok = xPattern.(*stringLiteralExpr); ok && !strings.ContainsRune(xInList.literal, '%') && xText.typ() == starlarkTypeList {
+		expr = xText
+	} else if xInList, ok = xText.(*stringLiteralExpr); ok {
+		expr = xPattern
+	} else {
+		return nil, false
 	}
-	if expr != nil && xInList != nil {
-		slExpr := newStringListExpr(strings.Fields(xInList.literal))
-		// Generate simpler code for the common cases:
-		if expr.typ() == starlarkTypeList {
-			if len(slExpr.items) == 1 {
-				// Checking that a string belongs to list
-				return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}
-			} else {
-				// TODO(asmundak):
-				panic("TBD")
-			}
-		} else if len(slExpr.items) == 1 {
-			return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}
+	slExpr := newStringListExpr(strings.Fields(xInList.literal))
+	// Generate simpler code for the common cases:
+	if expr.typ() == starlarkTypeList {
+		if len(slExpr.items) == 1 {
+			// Checking that a string belongs to list
+			return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}, true
 		} else {
-			return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}
+			return nil, false
 		}
+	} else if len(slExpr.items) == 1 {
+		return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}, true
+	} else {
+		return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}, true
 	}
-	return ctx.newBadExpr(cond, "filter arguments are too complex: %s", cond.Dump())
 }
 
 func (ctx *parseContext) parseCompareWildcardFuncResult(directive *mkparser.Directive,
@@ -1265,9 +1174,9 @@
 	if !isEmptyString(xValue) {
 		return ctx.newBadExpr(directive, "wildcard result can be compared only to empty: %s", xValue)
 	}
-	callFunc := wildcardExistsPhony
+	callFunc := baseName + ".file_wildcard_exists"
 	if s, ok := xCall.args[0].(*stringLiteralExpr); ok && !strings.ContainsAny(s.literal, "*?{[") {
-		callFunc = fileExistsPhony
+		callFunc = baseName + ".file_exists"
 	}
 	var cc starlarkExpr = &callExpr{name: callFunc, args: xCall.args, returnType: starlarkTypeBool}
 	if !negate {
@@ -1289,8 +1198,21 @@
 			right: &intLiteralExpr{-1},
 			isEq:  !negate,
 		}
+	} else if s, ok := maybeString(xValue); ok {
+		if s2, ok := maybeString(xCall.args[0]); ok && s == s2 {
+			return &eqExpr{
+				left: &callExpr{
+					object:     xCall.args[1],
+					name:       "find",
+					args:       []starlarkExpr{xCall.args[0]},
+					returnType: starlarkTypeInt,
+				},
+				right: &intLiteralExpr{-1},
+				isEq:  negate,
+			}
+		}
 	}
-	return ctx.newBadExpr(directive, "findstring result can be compared only to empty: %s", xValue)
+	return ctx.newBadExpr(directive, "$(findstring) can only be compared to nothing or its first argument")
 }
 
 func (ctx *parseContext) parseCompareStripFuncResult(directive *mkparser.Directive,
@@ -1321,37 +1243,51 @@
 
 	// If it is a single word, it can be a simple variable
 	// reference or a function call
-	if len(words) == 1 {
-		if isMakeControlFunc(refDump) || refDump == "shell" {
-			return &callExpr{
-				name:       refDump,
-				args:       []starlarkExpr{&stringLiteralExpr{""}},
-				returnType: starlarkTypeUnknown,
-			}
-		}
+	if len(words) == 1 && !isMakeControlFunc(refDump) && refDump != "shell" {
 		if strings.HasPrefix(refDump, soongNsPrefix) {
 			// TODO (asmundak): if we find many, maybe handle them.
 			return ctx.newBadExpr(node, "SOONG_CONFIG_ variables cannot be referenced, use soong_config_get instead: %s", refDump)
 		}
+		// Handle substitution references: https://www.gnu.org/software/make/manual/html_node/Substitution-Refs.html
+		if strings.Contains(refDump, ":") {
+			parts := strings.SplitN(refDump, ":", 2)
+			substParts := strings.SplitN(parts[1], "=", 2)
+			if len(substParts) < 2 || strings.Count(substParts[0], "%") > 1 {
+				return ctx.newBadExpr(node, "Invalid substitution reference")
+			}
+			if !strings.Contains(substParts[0], "%") {
+				if strings.Contains(substParts[1], "%") {
+					return ctx.newBadExpr(node, "A substitution reference must have a %% in the \"before\" part of the substitution if it has one in the \"after\" part.")
+				}
+				substParts[0] = "%" + substParts[0]
+				substParts[1] = "%" + substParts[1]
+			}
+			v := ctx.addVariable(parts[0])
+			if v == nil {
+				return ctx.newBadExpr(node, "unknown variable %s", refDump)
+			}
+			return &callExpr{
+				name:       baseName + ".mkpatsubst",
+				returnType: starlarkTypeString,
+				args: []starlarkExpr{
+					&stringLiteralExpr{literal: substParts[0]},
+					&stringLiteralExpr{literal: substParts[1]},
+					NewVariableRefExpr(v, ctx.lastAssignment(v.name()) != nil),
+				},
+			}
+		}
 		if v := ctx.addVariable(refDump); v != nil {
-			return &variableRefExpr{v, ctx.lastAssignment(v.name()) != nil}
+			return NewVariableRefExpr(v, ctx.lastAssignment(v.name()) != nil)
 		}
 		return ctx.newBadExpr(node, "unknown variable %s", refDump)
 	}
 
 	expr := &callExpr{name: words[0].Dump(), returnType: starlarkTypeUnknown}
-	args := words[1]
-	args.TrimLeftSpaces()
-	// Make control functions and shell need special treatment as everything
-	// after the name is a single text argument
-	if isMakeControlFunc(expr.name) || expr.name == "shell" {
-		x := ctx.parseMakeString(node, args)
-		if xBad, ok := x.(*badExpr); ok {
-			return xBad
-		}
-		expr.args = []starlarkExpr{x}
-		return expr
+	args := mkparser.SimpleMakeString("", words[0].Pos())
+	if len(words) >= 2 {
+		args = words[1]
 	}
+	args.TrimLeftSpaces()
 	if expr.name == "call" {
 		words = args.SplitN(",", 2)
 		if words[0].Empty() || !words[0].Const() {
@@ -1365,37 +1301,128 @@
 		}
 	}
 	if kf, found := knownFunctions[expr.name]; found {
-		expr.returnType = kf.returnType
+		return kf.parse(ctx, node, args)
 	} else {
 		return ctx.newBadExpr(node, "cannot handle invoking %s", expr.name)
 	}
-	switch expr.name {
-	case "word":
-		return ctx.parseWordFunc(node, args)
-	case "firstword", "lastword":
-		return ctx.parseFirstOrLastwordFunc(node, expr.name, args)
-	case "my-dir":
-		return &variableRefExpr{ctx.addVariable("LOCAL_PATH"), true}
-	case "subst", "patsubst":
-		return ctx.parseSubstFunc(node, expr.name, args)
-	default:
-		for _, arg := range args.Split(",") {
-			arg.TrimLeftSpaces()
-			arg.TrimRightSpaces()
-			x := ctx.parseMakeString(node, arg)
-			if xBad, ok := x.(*badExpr); ok {
-				return xBad
-			}
-			expr.args = append(expr.args, x)
+}
+
+type simpleCallParser struct {
+	name       string
+	returnType starlarkType
+	addGlobals bool
+}
+
+func (p *simpleCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	expr := &callExpr{name: p.name, returnType: p.returnType}
+	if p.addGlobals {
+		expr.args = append(expr.args, &globalsExpr{})
+	}
+	for _, arg := range args.Split(",") {
+		arg.TrimLeftSpaces()
+		arg.TrimRightSpaces()
+		x := ctx.parseMakeString(node, arg)
+		if xBad, ok := x.(*badExpr); ok {
+			return xBad
 		}
+		expr.args = append(expr.args, x)
 	}
 	return expr
 }
 
-func (ctx *parseContext) parseSubstFunc(node mkparser.Node, fname string, args *mkparser.MakeString) starlarkExpr {
+type makeControlFuncParser struct {
+	name string
+}
+
+func (p *makeControlFuncParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	// Make control functions need special treatment as everything
+	// after the name is a single text argument
+	x := ctx.parseMakeString(node, args)
+	if xBad, ok := x.(*badExpr); ok {
+		return xBad
+	}
+	return &callExpr{
+		name: p.name,
+		args: []starlarkExpr{
+			&stringLiteralExpr{ctx.script.mkFile},
+			x,
+		},
+		returnType: starlarkTypeUnknown,
+	}
+}
+
+type shellCallParser struct{}
+
+func (p *shellCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	// Shell functions need special treatment as everything
+	// after the name is a single text argument
+	x := ctx.parseMakeString(node, args)
+	if xBad, ok := x.(*badExpr); ok {
+		return xBad
+	}
+	return &callExpr{
+		name:       baseName + ".shell",
+		args:       []starlarkExpr{x},
+		returnType: starlarkTypeUnknown,
+	}
+}
+
+type myDirCallParser struct{}
+
+func (p *myDirCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	if !args.Empty() {
+		return ctx.newBadExpr(node, "my-dir function cannot have any arguments passed to it.")
+	}
+	return &variableRefExpr{ctx.addVariable("LOCAL_PATH"), true}
+}
+
+type isProductInListCallParser struct{}
+
+func (p *isProductInListCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	if args.Empty() {
+		return ctx.newBadExpr(node, "is-product-in-list requires an argument")
+	}
+	return &inExpr{
+		expr:  &variableRefExpr{ctx.addVariable("TARGET_PRODUCT"), true},
+		list:  maybeConvertToStringList(ctx.parseMakeString(node, args)),
+		isNot: false,
+	}
+}
+
+type isVendorBoardPlatformCallParser struct{}
+
+func (p *isVendorBoardPlatformCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	if args.Empty() || !identifierFullMatchRegex.MatchString(args.Dump()) {
+		return ctx.newBadExpr(node, "cannot handle non-constant argument to is-vendor-board-platform")
+	}
+	return &inExpr{
+		expr:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
+		list:  &variableRefExpr{ctx.addVariable(args.Dump() + "_BOARD_PLATFORMS"), true},
+		isNot: false,
+	}
+}
+
+type isVendorBoardQcomCallParser struct{}
+
+func (p *isVendorBoardQcomCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	if !args.Empty() {
+		return ctx.newBadExpr(node, "is-vendor-board-qcom does not accept any arguments")
+	}
+	return &inExpr{
+		expr:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
+		list:  &variableRefExpr{ctx.addVariable("QCOM_BOARD_PLATFORMS"), true},
+		isNot: false,
+	}
+}
+
+type substCallParser struct {
+	fname string
+}
+
+func (p *substCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
 	words := args.Split(",")
 	if len(words) != 3 {
-		return ctx.newBadExpr(node, "%s function should have 3 arguments", fname)
+		return ctx.newBadExpr(node, "%s function should have 3 arguments", p.fname)
 	}
 	from := ctx.parseMakeString(node, words[0])
 	if xBad, ok := from.(*badExpr); ok {
@@ -1409,7 +1436,7 @@
 	words[2].TrimRightSpaces()
 	obj := ctx.parseMakeString(node, words[2])
 	typ := obj.typ()
-	if typ == starlarkTypeString && fname == "subst" {
+	if typ == starlarkTypeString && p.fname == "subst" {
 		// Optimization: if it's $(subst from, to, string), emit string.replace(from, to)
 		return &callExpr{
 			object:     obj,
@@ -1419,13 +1446,80 @@
 		}
 	}
 	return &callExpr{
-		name:       fname,
+		name:       baseName + ".mk" + p.fname,
 		args:       []starlarkExpr{from, to, obj},
 		returnType: obj.typ(),
 	}
 }
 
-func (ctx *parseContext) parseWordFunc(node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+type ifCallParser struct{}
+
+func (p *ifCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	words := args.Split(",")
+	if len(words) != 2 && len(words) != 3 {
+		return ctx.newBadExpr(node, "if function should have 2 or 3 arguments, found "+strconv.Itoa(len(words)))
+	}
+	condition := ctx.parseMakeString(node, words[0])
+	ifTrue := ctx.parseMakeString(node, words[1])
+	var ifFalse starlarkExpr
+	if len(words) == 3 {
+		ifFalse = ctx.parseMakeString(node, words[2])
+	} else {
+		switch ifTrue.typ() {
+		case starlarkTypeList:
+			ifFalse = &listExpr{items: []starlarkExpr{}}
+		case starlarkTypeInt:
+			ifFalse = &intLiteralExpr{literal: 0}
+		case starlarkTypeBool:
+			ifFalse = &boolLiteralExpr{literal: false}
+		default:
+			ifFalse = &stringLiteralExpr{literal: ""}
+		}
+	}
+	return &ifExpr{
+		condition,
+		ifTrue,
+		ifFalse,
+	}
+}
+
+type foreachCallPaser struct{}
+
+func (p *foreachCallPaser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	words := args.Split(",")
+	if len(words) != 3 {
+		return ctx.newBadExpr(node, "foreach function should have 3 arguments, found "+strconv.Itoa(len(words)))
+	}
+	if !words[0].Const() || words[0].Empty() || !identifierFullMatchRegex.MatchString(words[0].Strings[0]) {
+		return ctx.newBadExpr(node, "first argument to foreach function must be a simple string identifier")
+	}
+	loopVarName := words[0].Strings[0]
+	list := ctx.parseMakeString(node, words[1])
+	action := ctx.parseMakeString(node, words[2]).transform(func(expr starlarkExpr) starlarkExpr {
+		if varRefExpr, ok := expr.(*variableRefExpr); ok && varRefExpr.ref.name() == loopVarName {
+			return &identifierExpr{loopVarName}
+		}
+		return nil
+	})
+
+	if list.typ() != starlarkTypeList {
+		list = &callExpr{
+			name:       baseName + ".words",
+			returnType: starlarkTypeList,
+			args:       []starlarkExpr{list},
+		}
+	}
+
+	return &foreachExpr{
+		varName: loopVarName,
+		list:    list,
+		action:  action,
+	}
+}
+
+type wordCallParser struct{}
+
+func (p *wordCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
 	words := args.Split(",")
 	if len(words) != 2 {
 		return ctx.newBadExpr(node, "word function should have 2 arguments")
@@ -1446,16 +1540,20 @@
 	if array.typ() != starlarkTypeList {
 		array = &callExpr{object: array, name: "split", returnType: starlarkTypeList}
 	}
-	return indexExpr{array, &intLiteralExpr{int(index - 1)}}
+	return &indexExpr{array, &intLiteralExpr{int(index - 1)}}
 }
 
-func (ctx *parseContext) parseFirstOrLastwordFunc(node mkparser.Node, name string, args *mkparser.MakeString) starlarkExpr {
+type firstOrLastwordCallParser struct {
+	isLastWord bool
+}
+
+func (p *firstOrLastwordCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
 	arg := ctx.parseMakeString(node, args)
 	if bad, ok := arg.(*badExpr); ok {
 		return bad
 	}
 	index := &intLiteralExpr{0}
-	if name == "lastword" {
+	if p.isLastWord {
 		if v, ok := arg.(*variableRefExpr); ok && v.ref.name() == "MAKEFILE_LIST" {
 			return &stringLiteralExpr{ctx.script.mkFile}
 		}
@@ -1467,6 +1565,68 @@
 	return &indexExpr{&callExpr{object: arg, name: "split", returnType: starlarkTypeList}, index}
 }
 
+func parseIntegerArguments(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString, expectedArgs int) ([]starlarkExpr, error) {
+	parsedArgs := make([]starlarkExpr, 0)
+	for _, arg := range args.Split(",") {
+		expr := ctx.parseMakeString(node, arg)
+		if expr.typ() == starlarkTypeList {
+			return nil, fmt.Errorf("argument to math argument has type list, which cannot be converted to int")
+		}
+		if s, ok := maybeString(expr); ok {
+			intVal, err := strconv.Atoi(strings.TrimSpace(s))
+			if err != nil {
+				return nil, err
+			}
+			expr = &intLiteralExpr{literal: intVal}
+		} else if expr.typ() != starlarkTypeInt {
+			expr = &callExpr{
+				name:       "int",
+				args:       []starlarkExpr{expr},
+				returnType: starlarkTypeInt,
+			}
+		}
+		parsedArgs = append(parsedArgs, expr)
+	}
+	if len(parsedArgs) != expectedArgs {
+		return nil, fmt.Errorf("function should have %d arguments", expectedArgs)
+	}
+	return parsedArgs, nil
+}
+
+type mathComparisonCallParser struct {
+	op string
+}
+
+func (p *mathComparisonCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	parsedArgs, err := parseIntegerArguments(ctx, node, args, 2)
+	if err != nil {
+		return ctx.newBadExpr(node, err.Error())
+	}
+	return &binaryOpExpr{
+		left:       parsedArgs[0],
+		right:      parsedArgs[1],
+		op:         p.op,
+		returnType: starlarkTypeBool,
+	}
+}
+
+type mathMaxOrMinCallParser struct {
+	function string
+}
+
+func (p *mathMaxOrMinCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	parsedArgs, err := parseIntegerArguments(ctx, node, args, 2)
+	if err != nil {
+		return ctx.newBadExpr(node, err.Error())
+	}
+	return &callExpr{
+		object:     nil,
+		name:       p.function,
+		args:       parsedArgs,
+		returnType: starlarkTypeInt,
+	}
+}
+
 func (ctx *parseContext) parseMakeString(node mkparser.Node, mk *mkparser.MakeString) starlarkExpr {
 	if mk.Const() {
 		return &stringLiteralExpr{mk.Dump()}
@@ -1477,50 +1637,70 @@
 	// If we reached here, it's neither string literal nor a simple variable,
 	// we need a full-blown interpolation node that will generate
 	// "a%b%c" % (X, Y) for a$(X)b$(Y)c
-	xInterp := &interpolateExpr{args: make([]starlarkExpr, len(mk.Variables))}
-	for i, ref := range mk.Variables {
-		arg := ctx.parseReference(node, ref.Name)
-		if x, ok := arg.(*badExpr); ok {
-			return x
+	parts := make([]starlarkExpr, len(mk.Variables)+len(mk.Strings))
+	for i := 0; i < len(parts); i++ {
+		if i%2 == 0 {
+			parts[i] = &stringLiteralExpr{literal: mk.Strings[i/2]}
+		} else {
+			parts[i] = ctx.parseReference(node, mk.Variables[i/2].Name)
+			if x, ok := parts[i].(*badExpr); ok {
+				return x
+			}
 		}
-		xInterp.args[i] = arg
 	}
-	xInterp.chunks = append(xInterp.chunks, mk.Strings...)
-	return xInterp
+	return NewInterpolateExpr(parts)
 }
 
 // Handles the statements whose treatment is the same in all contexts: comment,
 // assignment, variable (which is a macro call in reality) and all constructs that
 // do not handle in any context ('define directive and any unrecognized stuff).
-func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) {
+func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) []starlarkNode {
+	var result []starlarkNode
 	switch x := node.(type) {
 	case *mkparser.Comment:
-		ctx.maybeHandleAnnotation(x)
-		ctx.insertComment("#" + x.Comment)
+		if n, handled := ctx.maybeHandleAnnotation(x); handled && n != nil {
+			result = []starlarkNode{n}
+		} else if !handled {
+			result = []starlarkNode{&commentNode{strings.TrimSpace("#" + x.Comment)}}
+		}
 	case *mkparser.Assignment:
-		ctx.handleAssignment(x)
+		result = ctx.handleAssignment(x)
 	case *mkparser.Variable:
-		ctx.handleVariable(x)
+		result = ctx.handleVariable(x)
 	case *mkparser.Directive:
 		switch x.Name {
 		case "define":
-			ctx.handleDefine(x)
+			if res := ctx.maybeHandleDefine(x); res != nil {
+				result = []starlarkNode{res}
+			}
 		case "include", "-include":
-			ctx.handleInclude(node, ctx.parseMakeString(node, x.Args), x.Name[0] != '-')
+			result = ctx.handleInclude(node, ctx.parseMakeString(node, x.Args), x.Name[0] != '-')
 		case "ifeq", "ifneq", "ifdef", "ifndef":
-			ctx.handleIfBlock(x)
+			result = []starlarkNode{ctx.handleIfBlock(x)}
 		default:
-			ctx.errorf(x, "unexpected directive %s", x.Name)
+			result = []starlarkNode{ctx.newBadNode(x, "unexpected directive %s", x.Name)}
 		}
 	default:
-		ctx.errorf(x, "unsupported line %s", strings.ReplaceAll(x.Dump(), "\n", "\n#"))
+		result = []starlarkNode{ctx.newBadNode(x, "unsupported line %s", strings.ReplaceAll(x.Dump(), "\n", "\n#"))}
 	}
+
+	// Clear the includeTops after each non-comment statement
+	// so that include annotations placed on certain statements don't apply
+	// globally for the rest of the makefile was well.
+	if _, wasComment := node.(*mkparser.Comment); !wasComment && len(ctx.includeTops) > 0 {
+		ctx.includeTops = []string{}
+	}
+
+	if result == nil {
+		result = []starlarkNode{}
+	}
+	return result
 }
 
 // Processes annotation. An annotation is a comment that starts with #RBC# and provides
 // a conversion hint -- say, where to look for the dynamically calculated inherit/include
-// paths.
-func (ctx *parseContext) maybeHandleAnnotation(cnode *mkparser.Comment) {
+// paths. Returns true if the comment was a successfully-handled annotation.
+func (ctx *parseContext) maybeHandleAnnotation(cnode *mkparser.Comment) (starlarkNode, bool) {
 	maybeTrim := func(s, prefix string) (string, bool) {
 		if strings.HasPrefix(s, prefix) {
 			return strings.TrimSpace(strings.TrimPrefix(s, prefix)), true
@@ -1529,37 +1709,20 @@
 	}
 	annotation, ok := maybeTrim(cnode.Comment, annotationCommentPrefix)
 	if !ok {
-		return
+		return nil, false
 	}
 	if p, ok := maybeTrim(annotation, "include_top"); ok {
+		// Don't allow duplicate include tops, because then we will generate
+		// invalid starlark code. (duplicate keys in the _entry dictionary)
+		for _, top := range ctx.includeTops {
+			if top == p {
+				return nil, true
+			}
+		}
 		ctx.includeTops = append(ctx.includeTops, p)
-		return
+		return nil, true
 	}
-	ctx.errorf(cnode, "unsupported annotation %s", cnode.Comment)
-
-}
-
-func (ctx *parseContext) insertComment(s string) {
-	ctx.receiver.newNode(&commentNode{strings.TrimSpace(s)})
-}
-
-func (ctx *parseContext) carryAsComment(failedNode mkparser.Node) {
-	for _, line := range strings.Split(failedNode.Dump(), "\n") {
-		ctx.insertComment("# " + line)
-	}
-}
-
-// records that the given node failed to be converted and includes an explanatory message
-func (ctx *parseContext) errorf(failedNode mkparser.Node, message string, args ...interface{}) {
-	if ctx.errorLogger != nil {
-		ctx.errorLogger.NewError(ctx.errorLocation(failedNode), failedNode, message, args...)
-	}
-	ctx.receiver.newNode(&exprNode{ctx.newBadExpr(failedNode, message, args...)})
-	ctx.script.hasErrors = true
-}
-
-func (ctx *parseContext) wrapBadExpr(xBad *badExpr) {
-	ctx.receiver.newNode(&exprNode{xBad})
+	return ctx.newBadNode(cnode, "unsupported annotation %s", cnode.Comment), true
 }
 
 func (ctx *parseContext) loadedModulePath(path string) string {
@@ -1669,11 +1832,11 @@
 	starScript := &StarlarkScript{
 		moduleName:     moduleNameForFile(req.MkFile),
 		mkFile:         req.MkFile,
-		topDir:         req.RootDir,
 		traceCalls:     req.TraceCalls,
 		sourceFS:       req.SourceFS,
 		makefileFinder: req.MakefileFinder,
 		nodeLocator:    func(pos mkparser.Pos) int { return parser.Unpack(pos).Line },
+		nodes:          make([]starlarkNode, 0),
 	}
 	ctx := newParseContext(starScript, nodes)
 	ctx.outputSuffix = req.OutputSuffix
@@ -1685,9 +1848,8 @@
 			ctx.tracedVariables[v] = true
 		}
 	}
-	ctx.pushReceiver(starScript)
 	for ctx.hasNodes() && ctx.fatalError == nil {
-		ctx.handleSimpleStatement(ctx.getNode())
+		starScript.nodes = append(starScript.nodes, ctx.handleSimpleStatement(ctx.getNode())...)
 	}
 	if ctx.fatalError != nil {
 		return nil, ctx.fatalError
@@ -1695,12 +1857,12 @@
 	return starScript, nil
 }
 
-func Launcher(mainModuleUri, versionDefaultsUri, mainModuleName string) string {
+func Launcher(mainModuleUri, inputVariablesUri, mainModuleName string) string {
 	var buf bytes.Buffer
 	fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName)
-	fmt.Fprintf(&buf, "load(%q, \"version_defaults\")\n", versionDefaultsUri)
+	fmt.Fprintf(&buf, "load(%q, input_variables_init = \"init\")\n", inputVariablesUri)
 	fmt.Fprintf(&buf, "load(%q, \"init\")\n", mainModuleUri)
-	fmt.Fprintf(&buf, "%s(%s(%q, init, version_defaults))\n", cfnPrintVars, cfnMain, mainModuleName)
+	fmt.Fprintf(&buf, "%s(%s(%q, init, input_variables_init))\n", cfnPrintVars, cfnMain, mainModuleName)
 	return buf.String()
 }
 
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index dfdf274..447f658 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -121,7 +121,7 @@
 ifdef PRODUCT_NAME
 $(call inherit-product, part1.mk)
 else # Comment
-$(call inherit-product, $(LOCAL_PATH)/part1.mk)
+$(call inherit-product, $(LOCAL_PATH)/part.mk)
 endif
 `,
 		expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -131,11 +131,13 @@
 def init(g, handle):
   cfg = rblf.cfg(handle)
   rblf.inherit(handle, "part", _part_init)
-  if g.get("PRODUCT_NAME") != None:
+  if cfg.get("PRODUCT_NAME", ""):
+    if not _part1_init:
+      rblf.mkerror("product.mk", "Cannot find %s" % (":part1.star"))
     rblf.inherit(handle, "part1", _part1_init)
   else:
     # Comment
-    rblf.inherit(handle, "part1", _part1_init)
+    rblf.inherit(handle, "part", _part_init)
 `,
 	},
 	{
@@ -172,7 +174,9 @@
 def init(g, handle):
   cfg = rblf.cfg(handle)
   _part_init(g, handle)
-  if g.get("PRODUCT_NAME") != None:
+  if cfg.get("PRODUCT_NAME", ""):
+    if not _part1_init:
+      rblf.mkerror("product.mk", "Cannot find %s" % (":part1.star"))
     _part1_init(g, handle)
   else:
     if _part1_init != None:
@@ -227,7 +231,7 @@
 
 def init(g, handle):
   cfg = rblf.cfg(handle)
-  if g.get("PRODUCT_NAME") != None:
+  if cfg.get("PRODUCT_NAME", ""):
     cfg["PRODUCT_NAME"] = "gizmo"
   else:
     pass
@@ -271,7 +275,7 @@
 
 def init(g, handle):
   cfg = rblf.cfg(handle)
-  if g.get("PRODUCT_NAME") != None:
+  if cfg.get("PRODUCT_NAME", ""):
     # Comment
     pass
   else:
@@ -292,7 +296,7 @@
 
 def init(g, handle):
   cfg = rblf.cfg(handle)
-  if not g.get("PRODUCT_NAME") != None:
+  if not cfg.get("PRODUCT_NAME", ""):
     cfg["PRODUCT_NAME"] = "gizmo1"
   else:
     cfg["PRODUCT_NAME"] = "gizmo2"
@@ -311,9 +315,9 @@
 
 def init(g, handle):
   cfg = rblf.cfg(handle)
-  if g.get("PRODUCT_NAME") != None:
+  if cfg.get("PRODUCT_NAME", ""):
     cfg["PRODUCT_NAME"] = "gizmo"
-  elif not g.get("PRODUCT_PACKAGES") != None:
+  elif not cfg.get("PRODUCT_PACKAGES", []):
     # Comment
     pass
 `,
@@ -385,11 +389,17 @@
 endif
 ifeq ($(TARGET_BUILD_VARIANT), $(filter $(TARGET_BUILD_VARIANT), userdebug eng))
 endif
+ifneq (, $(filter $(TARGET_BUILD_VARIANT), userdebug eng))
+endif
+ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
+endif
 ifneq (,$(filter true, $(v1)$(v2)))
 endif
 ifeq (,$(filter barbet coral%,$(TARGET_PRODUCT)))
 else ifneq (,$(filter barbet%,$(TARGET_PRODUCT)))
 endif
+ifeq (,$(filter-out sunfish_kasan, $(TARGET_PRODUCT)))
+endif
 `,
 		expected: `load("//build/make/core:product_config.rbc", "rblf")
 
@@ -401,14 +411,20 @@
     pass
   if "plaf" in g.get("PLATFORM_LIST", []):
     pass
+  if g["TARGET_BUILD_VARIANT"] == " ".join(rblf.filter(g["TARGET_BUILD_VARIANT"], "userdebug eng")):
+    pass
   if g["TARGET_BUILD_VARIANT"] in ["userdebug", "eng"]:
     pass
+  if rblf.filter("userdebug eng", g["TARGET_BUILD_VARIANT"]):
+    pass
   if rblf.filter("true", "%s%s" % (_v1, _v2)):
     pass
   if not rblf.filter("barbet coral%", g["TARGET_PRODUCT"]):
     pass
   elif rblf.filter("barbet%", g["TARGET_PRODUCT"]):
     pass
+  if not rblf.filter_out("sunfish_kasan", g["TARGET_PRODUCT"]):
+    pass
 `,
 	},
 	{
@@ -501,11 +517,11 @@
 
 def init(g, handle):
   cfg = rblf.cfg(handle)
-  if g.get("PRODUCT_NAME") != None:
+  if cfg.get("PRODUCT_NAME", ""):
     cfg["PRODUCT_PACKAGES"] = ["pack-if0"]
-    if g.get("PRODUCT_MODEL") != None:
+    if cfg.get("PRODUCT_MODEL", ""):
       cfg["PRODUCT_PACKAGES"] = ["pack-if-if"]
-    elif g.get("PRODUCT_NAME") != None:
+    elif cfg.get("PRODUCT_NAME", ""):
       cfg["PRODUCT_PACKAGES"] = ["pack-if-elif"]
     else:
       cfg["PRODUCT_PACKAGES"] = ["pack-if-else"]
@@ -594,9 +610,9 @@
 
 def init(g, handle):
   cfg = rblf.cfg(handle)
-  if g.get("TARGET_BOARD_PLATFORM", "") in ["msm8998"]:
+  if rblf.board_platform_in(g, "msm8998"):
     pass
-  elif g.get("TARGET_BOARD_PLATFORM", "") != "copper":
+  elif not rblf.board_platform_is(g, "copper"):
     pass
   elif g.get("TARGET_BOARD_PLATFORM", "") not in g["QCOM_BOARD_PLATFORMS"]:
     pass
@@ -629,15 +645,42 @@
 		desc:   "findstring call",
 		mkname: "product.mk",
 		in: `
+result := $(findstring a,a b c)
+result := $(findstring b,x y z)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  _result = rblf.findstring("a", "a b c")
+  _result = rblf.findstring("b", "x y z")
+`,
+	},
+	{
+		desc:   "findstring in if statement",
+		mkname: "product.mk",
+		in: `
+ifeq ($(findstring foo,$(PRODUCT_PACKAGES)),)
+endif
 ifneq ($(findstring foo,$(PRODUCT_PACKAGES)),)
 endif
+ifeq ($(findstring foo,$(PRODUCT_PACKAGES)),foo)
+endif
+ifneq ($(findstring foo,$(PRODUCT_PACKAGES)),foo)
+endif
 `,
 		expected: `load("//build/make/core:product_config.rbc", "rblf")
 
 def init(g, handle):
   cfg = rblf.cfg(handle)
+  if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") == -1:
+    pass
   if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") != -1:
     pass
+  if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") != -1:
+    pass
+  if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") == -1:
+    pass
 `,
 	},
 	{
@@ -1013,12 +1056,12 @@
   cfg = rblf.cfg(handle)
   g["MY_PATH"] = "foo"
   _entry = {
-    "vendor/foo1/cfg.mk": ("_cfg", _cfg_init),
-    "vendor/bar/baz/cfg.mk": ("_cfg1", _cfg1_init),
+    "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
+    "vendor/bar/baz/cfg.mk": ("vendor/bar/baz/cfg", _cfg1_init),
   }.get("vendor/%s/cfg.mk" % g["MY_PATH"])
   (_varmod, _varmod_init) = _entry if _entry else (None, None)
   if not _varmod_init:
-    rblf.mkerror("cannot")
+    rblf.mkerror("product.mk", "Cannot find %s" % ("vendor/%s/cfg.mk" % g["MY_PATH"]))
   rblf.inherit(handle, _varmod, _varmod_init)
 `,
 	},
@@ -1036,13 +1079,62 @@
 def init(g, handle):
   cfg = rblf.cfg(handle)
   g["MY_PATH"] = "foo"
-  #RBC# include_top vendor/foo1
+  rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
+`,
+	},
+	{
+		desc:   "Dynamic inherit with duplicated hint",
+		mkname: "product.mk",
+		in: `
+MY_PATH:=foo
+#RBC# include_top vendor/foo1
+$(call inherit-product,$(MY_PATH)/cfg.mk)
+#RBC# include_top vendor/foo1
+#RBC# include_top vendor/foo1
+$(call inherit-product,$(MY_PATH)/cfg.mk)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+load("//vendor/foo1:cfg.star|init", _cfg_init = "init")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  g["MY_PATH"] = "foo"
+  rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
+  rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
+`,
+	},
+	{
+		desc:   "Dynamic inherit path that lacks hint",
+		mkname: "product.mk",
+		in: `
+#RBC# include_top foo
+$(call inherit-product,$(MY_VAR)/font.mk)
+
+#RBC# include_top foo
+
+# There's some space and even this comment between the include_top and the inherit-product
+
+$(call inherit-product,$(MY_VAR)/font.mk)
+
+$(call inherit-product,$(MY_VAR)/font.mk)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+load("//foo:font.star|init", _font_init = "init")
+load("//bar:font.star|init", _font1_init = "init")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  rblf.inherit(handle, "foo/font", _font_init)
+  # There's some space and even this comment between the include_top and the inherit-product
+  rblf.inherit(handle, "foo/font", _font_init)
+  rblf.mkwarning("product.mk:11", "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.")
   _entry = {
-    "vendor/foo1/cfg.mk": ("_cfg", _cfg_init),
-  }.get("%s/cfg.mk" % g["MY_PATH"])
+    "foo/font.mk": ("foo/font", _font_init),
+    "bar/font.mk": ("bar/font", _font1_init),
+  }.get("%s/font.mk" % g.get("MY_VAR", ""))
   (_varmod, _varmod_init) = _entry if _entry else (None, None)
   if not _varmod_init:
-    rblf.mkerror("cannot")
+    rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
   rblf.inherit(handle, _varmod, _varmod_init)
 `,
 	},
@@ -1069,7 +1161,6 @@
 def init(g, handle):
   cfg = rblf.cfg(handle)
   rblf.mk2rbc_error("product.mk:2", "cannot handle override directive")
-  g["override FOO"] = ""
 `,
 	},
 	{
@@ -1087,6 +1178,152 @@
     pass
 `,
 	},
+	{
+		desc:   "if expression",
+		mkname: "product.mk",
+		in: `
+TEST_VAR := foo
+TEST_VAR_LIST := foo
+TEST_VAR_LIST += bar
+TEST_VAR_2 := $(if $(TEST_VAR),bar)
+TEST_VAR_3 := $(if $(TEST_VAR),bar,baz)
+TEST_VAR_3 := $(if $(TEST_VAR),$(TEST_VAR_LIST))
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  g["TEST_VAR"] = "foo"
+  g["TEST_VAR_LIST"] = ["foo"]
+  g["TEST_VAR_LIST"] += ["bar"]
+  g["TEST_VAR_2"] = ("bar" if g["TEST_VAR"] else "")
+  g["TEST_VAR_3"] = ("bar" if g["TEST_VAR"] else "baz")
+  g["TEST_VAR_3"] = (g["TEST_VAR_LIST"] if g["TEST_VAR"] else [])
+`,
+	},
+	{
+		desc:   "substitution references",
+		mkname: "product.mk",
+		in: `
+SOURCES := foo.c bar.c
+OBJECTS := $(SOURCES:.c=.o)
+OBJECTS2 := $(SOURCES:%.c=%.o)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  g["SOURCES"] = "foo.c bar.c"
+  g["OBJECTS"] = rblf.mkpatsubst("%.c", "%.o", g["SOURCES"])
+  g["OBJECTS2"] = rblf.mkpatsubst("%.c", "%.o", g["SOURCES"])
+`,
+	},
+	{
+		desc:   "foreach expressions",
+		mkname: "product.mk",
+		in: `
+BOOT_KERNEL_MODULES := foo.ko bar.ko
+BOOT_KERNEL_MODULES_FILTER := $(foreach m,$(BOOT_KERNEL_MODULES),%/$(m))
+BOOT_KERNEL_MODULES_LIST := foo.ko
+BOOT_KERNEL_MODULES_LIST += bar.ko
+BOOT_KERNEL_MODULES_FILTER_2 := $(foreach m,$(BOOT_KERNEL_MODULES_LIST),%/$(m))
+
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  g["BOOT_KERNEL_MODULES"] = "foo.ko bar.ko"
+  g["BOOT_KERNEL_MODULES_FILTER"] = ["%%/%s" % m for m in rblf.words(g["BOOT_KERNEL_MODULES"])]
+  g["BOOT_KERNEL_MODULES_LIST"] = ["foo.ko"]
+  g["BOOT_KERNEL_MODULES_LIST"] += ["bar.ko"]
+  g["BOOT_KERNEL_MODULES_FILTER_2"] = ["%%/%s" % m for m in g["BOOT_KERNEL_MODULES_LIST"]]
+`,
+	},
+	{
+		desc:   "List appended to string",
+		mkname: "product.mk",
+		in: `
+NATIVE_BRIDGE_PRODUCT_PACKAGES := \
+    libnative_bridge_vdso.native_bridge \
+    native_bridge_guest_app_process.native_bridge \
+    native_bridge_guest_linker.native_bridge
+
+NATIVE_BRIDGE_MODIFIED_GUEST_LIBS := \
+    libaaudio \
+    libamidi \
+    libandroid \
+    libandroid_runtime
+
+NATIVE_BRIDGE_PRODUCT_PACKAGES += \
+    $(addsuffix .native_bridge,$(NATIVE_BRIDGE_ORIG_GUEST_LIBS))
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  g["NATIVE_BRIDGE_PRODUCT_PACKAGES"] = "libnative_bridge_vdso.native_bridge      native_bridge_guest_app_process.native_bridge      native_bridge_guest_linker.native_bridge"
+  g["NATIVE_BRIDGE_MODIFIED_GUEST_LIBS"] = "libaaudio      libamidi      libandroid      libandroid_runtime"
+  g["NATIVE_BRIDGE_PRODUCT_PACKAGES"] += " " + " ".join(rblf.addsuffix(".native_bridge", g.get("NATIVE_BRIDGE_ORIG_GUEST_LIBS", "")))
+`,
+	},
+	{
+		desc:   "Math functions",
+		mkname: "product.mk",
+		in: `
+# Test the math functions defined in build/make/common/math.mk
+ifeq ($(call math_max,2,5),5)
+endif
+ifeq ($(call math_min,2,5),2)
+endif
+ifeq ($(call math_gt_or_eq,2,5),true)
+endif
+ifeq ($(call math_gt,2,5),true)
+endif
+ifeq ($(call math_lt,2,5),true)
+endif
+ifeq ($(call math_gt_or_eq,2,5),)
+endif
+ifeq ($(call math_gt,2,5),)
+endif
+ifeq ($(call math_lt,2,5),)
+endif
+ifeq ($(call math_gt_or_eq,$(MY_VAR), 5),true)
+endif
+ifeq ($(call math_gt_or_eq,$(MY_VAR),$(MY_OTHER_VAR)),true)
+endif
+ifeq ($(call math_gt_or_eq,100$(MY_VAR),10),true)
+endif
+`,
+		expected: `# Test the math functions defined in build/make/common/math.mk
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if max(2, 5) == 5:
+    pass
+  if min(2, 5) == 2:
+    pass
+  if 2 >= 5:
+    pass
+  if 2 > 5:
+    pass
+  if 2 < 5:
+    pass
+  if 2 < 5:
+    pass
+  if 2 <= 5:
+    pass
+  if 2 >= 5:
+    pass
+  if int(g.get("MY_VAR", "")) >= 5:
+    pass
+  if int(g.get("MY_VAR", "")) >= int(g.get("MY_OTHER_VAR", "")):
+    pass
+  if int("100%s" % g.get("MY_VAR", "")) >= 10:
+    pass
+`,
+	},
 }
 
 var known_variables = []struct {
@@ -1158,7 +1395,6 @@
 				ss, err := Convert(Request{
 					MkFile:         test.mkname,
 					Reader:         bytes.NewBufferString(test.in),
-					RootDir:        ".",
 					OutputSuffix:   ".star",
 					SourceFS:       fs,
 					MakefileFinder: &testMakefileFinder{fs: fs},
diff --git a/mk2rbc/node.go b/mk2rbc/node.go
index d38299d..5d98d7b 100644
--- a/mk2rbc/node.go
+++ b/mk2rbc/node.go
@@ -47,17 +47,23 @@
 	originalPath    string // Makefile file path
 	moduleLocalName string
 	optional        bool
+	missing         bool // a module may not exist if a module that depends on it is loaded dynamically
 }
 
 func (im moduleInfo) entryName() string {
 	return im.moduleLocalName + "_init"
 }
 
+func (mi moduleInfo) name() string {
+	return fmt.Sprintf("%q", MakePath2ModuleName(mi.originalPath))
+}
+
 type inheritedModule interface {
 	name() string
 	entryName() string
 	emitSelect(gctx *generationContext)
-	shouldExist() bool
+	pathExpr() starlarkExpr
+	needsLoadCheck() bool
 }
 
 type inheritedStaticModule struct {
@@ -65,21 +71,23 @@
 	loadAlways bool
 }
 
-func (im inheritedStaticModule) name() string {
-	return fmt.Sprintf("%q", MakePath2ModuleName(im.originalPath))
-}
-
 func (im inheritedStaticModule) emitSelect(_ *generationContext) {
 }
 
-func (im inheritedStaticModule) shouldExist() bool {
-	return im.loadAlways
+func (im inheritedStaticModule) pathExpr() starlarkExpr {
+	return &stringLiteralExpr{im.path}
+}
+
+func (im inheritedStaticModule) needsLoadCheck() bool {
+	return im.missing
 }
 
 type inheritedDynamicModule struct {
 	path             interpolateExpr
 	candidateModules []*moduleInfo
 	loadAlways       bool
+	location         ErrorLocation
+	needsWarning     bool
 }
 
 func (i inheritedDynamicModule) name() string {
@@ -91,12 +99,16 @@
 }
 
 func (i inheritedDynamicModule) emitSelect(gctx *generationContext) {
+	if i.needsWarning {
+		gctx.newLine()
+		gctx.writef("%s.mkwarning(%q, %q)", baseName, i.location, "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.")
+	}
 	gctx.newLine()
 	gctx.writef("_entry = {")
 	gctx.indentLevel++
 	for _, mi := range i.candidateModules {
 		gctx.newLine()
-		gctx.writef(`"%s": (%q, %s),`, mi.originalPath, mi.moduleLocalName, mi.entryName())
+		gctx.writef(`"%s": (%s, %s),`, mi.originalPath, mi.name(), mi.entryName())
 	}
 	gctx.indentLevel--
 	gctx.newLine()
@@ -105,18 +117,14 @@
 	gctx.write(")")
 	gctx.newLine()
 	gctx.writef("(%s, %s) = _entry if _entry else (None, None)", i.name(), i.entryName())
-	if i.loadAlways {
-		gctx.newLine()
-		gctx.writef("if not %s:", i.entryName())
-		gctx.indentLevel++
-		gctx.newLine()
-		gctx.write(`rblf.mkerror("cannot")`)
-		gctx.indentLevel--
-	}
 }
 
-func (i inheritedDynamicModule) shouldExist() bool {
-	return i.loadAlways
+func (i inheritedDynamicModule) pathExpr() starlarkExpr {
+	return &i.path
+}
+
+func (i inheritedDynamicModule) needsLoadCheck() bool {
+	return true
 }
 
 type inheritNode struct {
@@ -126,20 +134,22 @@
 
 func (inn *inheritNode) emit(gctx *generationContext) {
 	// Unconditional case:
+	//    maybe check that loaded
 	//    rblf.inherit(handle, <module>, module_init)
 	// Conditional case:
 	//    if <module>_init != None:
 	//      same as above
 	inn.module.emitSelect(gctx)
-
 	name := inn.module.name()
 	entry := inn.module.entryName()
-	gctx.newLine()
 	if inn.loadAlways {
+		gctx.emitLoadCheck(inn.module)
+		gctx.newLine()
 		gctx.writef("%s(handle, %s, %s)", cfnInherit, name, entry)
 		return
 	}
 
+	gctx.newLine()
 	gctx.writef("if %s:", entry)
 	gctx.indentLevel++
 	gctx.newLine()
@@ -155,12 +165,14 @@
 func (inn *includeNode) emit(gctx *generationContext) {
 	inn.module.emitSelect(gctx)
 	entry := inn.module.entryName()
-	gctx.newLine()
 	if inn.loadAlways {
+		gctx.emitLoadCheck(inn.module)
+		gctx.newLine()
 		gctx.writef("%s(g, handle)", entry)
 		return
 	}
 
+	gctx.newLine()
 	gctx.writef("if %s != None:", entry)
 	gctx.indentLevel++
 	gctx.newLine()
@@ -243,29 +255,17 @@
 	nodes []starlarkNode
 }
 
-func (cb *switchCase) newNode(node starlarkNode) {
-	cb.nodes = append(cb.nodes, node)
-}
-
 func (cb *switchCase) emit(gctx *generationContext) {
 	cb.gate.emit(gctx)
 	gctx.indentLevel++
 	hasStatements := false
-	emitNode := func(node starlarkNode) {
+	for _, node := range cb.nodes {
 		if _, ok := node.(*commentNode); !ok {
 			hasStatements = true
 		}
 		node.emit(gctx)
 	}
-	if len(cb.nodes) > 0 {
-		emitNode(cb.nodes[0])
-		for _, node := range cb.nodes[1:] {
-			emitNode(node)
-		}
-		if !hasStatements {
-			gctx.emitPass()
-		}
-	} else {
+	if !hasStatements {
 		gctx.emitPass()
 	}
 	gctx.indentLevel--
@@ -276,22 +276,8 @@
 	ssCases []*switchCase
 }
 
-func (ssw *switchNode) newNode(node starlarkNode) {
-	switch br := node.(type) {
-	case *switchCase:
-		ssw.ssCases = append(ssw.ssCases, br)
-	default:
-		panic(fmt.Errorf("expected switchCase node, got %t", br))
-	}
-}
-
 func (ssw *switchNode) emit(gctx *generationContext) {
-	if len(ssw.ssCases) == 0 {
-		gctx.emitPass()
-	} else {
-		ssw.ssCases[0].emit(gctx)
-		for _, ssCase := range ssw.ssCases[1:] {
-			ssCase.emit(gctx)
-		}
+	for _, ssCase := range ssw.ssCases {
+		ssCase.emit(gctx)
 	}
 }
diff --git a/mk2rbc/variable.go b/mk2rbc/variable.go
index 6b67a7c..f7adca5 100644
--- a/mk2rbc/variable.go
+++ b/mk2rbc/variable.go
@@ -81,10 +81,12 @@
 	emitAppend := func() {
 		pcv.emitGet(gctx, true)
 		gctx.write(" += ")
+		value := asgn.value
 		if pcv.valueType() == starlarkTypeString {
 			gctx.writef(`" " + `)
+			value = &toStringExpr{expr: value}
 		}
-		asgn.value.emit(gctx)
+		value.emit(gctx)
 	}
 
 	switch asgn.flavor {
@@ -136,10 +138,12 @@
 	emitAppend := func() {
 		scv.emitGet(gctx, true)
 		gctx.write(" += ")
+		value := asgn.value
 		if scv.valueType() == starlarkTypeString {
 			gctx.writef(`" " + `)
+			value = &toStringExpr{expr: value}
 		}
-		asgn.value.emit(gctx)
+		value.emit(gctx)
 	}
 
 	switch asgn.flavor {
@@ -193,10 +197,12 @@
 	case asgnAppend:
 		lv.emitGet(gctx, false)
 		gctx.write(" += ")
+		value := asgn.value
 		if lv.valueType() == starlarkTypeString {
 			gctx.writef(`" " + `)
+			value = &toStringExpr{expr: value}
 		}
-		asgn.value.emit(gctx)
+		value.emit(gctx)
 	case asgnMaybeAppend:
 		gctx.writef("%s(%q, ", cfnLocalAppend, lv)
 		asgn.value.emit(gctx)
diff --git a/mk2rbc/version_defaults.go b/mk2rbc/version_defaults.go
deleted file mode 100644
index 64645d7..0000000
--- a/mk2rbc/version_defaults.go
+++ /dev/null
@@ -1,113 +0,0 @@
-// 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 (
-	"bytes"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"sort"
-	"strconv"
-	"strings"
-
-	mkparser "android/soong/androidmk/parser"
-)
-
-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)
-	sort.Strings(codenames)
-
-	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
deleted file mode 100644
index c78fa32..0000000
--- a/mk2rbc/version_defaults_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-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/androidmk.go b/python/androidmk.go
index ccc85ec..233d867 100644
--- a/python/androidmk.go
+++ b/python/androidmk.go
@@ -78,7 +78,7 @@
 	entries.Required = append(entries.Required, "libc++")
 	entries.ExtraEntries = append(entries.ExtraEntries,
 		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-			path, file := filepath.Split(installer.path.ToMakePath().String())
+			path, file := filepath.Split(installer.path.String())
 			stem := strings.TrimSuffix(file, filepath.Ext(file))
 
 			entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
diff --git a/python/binary.go b/python/binary.go
index af02de6..99c6259 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -27,7 +27,6 @@
 
 func init() {
 	registerPythonBinaryComponents(android.InitRegistrationContext)
-	android.RegisterBp2BuildMutator("python_binary_host", PythonBinaryBp2Build)
 }
 
 func registerPythonBinaryComponents(ctx android.RegistrationContext) {
@@ -41,17 +40,7 @@
 	Python_version *string
 }
 
-func PythonBinaryBp2Build(ctx android.TopDownMutatorContext) {
-	m, ok := ctx.Module().(*Module)
-	if !ok || !m.ConvertWithBp2build(ctx) {
-		return
-	}
-
-	// a Module can be something other than a python_binary_host
-	if ctx.ModuleType() != "python_binary_host" {
-		return
-	}
-
+func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
 	var main *string
 	for _, propIntf := range m.GetProperties() {
 		if props, ok := propIntf.(*BinaryProperties); ok {
diff --git a/python/library.go b/python/library.go
index b920117..d026c13 100644
--- a/python/library.go
+++ b/python/library.go
@@ -17,8 +17,6 @@
 // This file contains the module types for building Python library.
 
 import (
-	"fmt"
-
 	"android/soong/android"
 	"android/soong/bazel"
 
@@ -27,8 +25,6 @@
 
 func init() {
 	registerPythonLibraryComponents(android.InitRegistrationContext)
-	android.RegisterBp2BuildMutator("python_library_host", PythonLibraryHostBp2Build)
-	android.RegisterBp2BuildMutator("python_library", PythonLibraryBp2Build)
 }
 
 func registerPythonLibraryComponents(ctx android.RegistrationContext) {
@@ -50,25 +46,7 @@
 	Srcs_version *string
 }
 
-func PythonLibraryHostBp2Build(ctx android.TopDownMutatorContext) {
-	pythonLibBp2Build(ctx, "python_library_host")
-}
-
-func PythonLibraryBp2Build(ctx android.TopDownMutatorContext) {
-	pythonLibBp2Build(ctx, "python_library")
-}
-
-func pythonLibBp2Build(ctx android.TopDownMutatorContext, modType string) {
-	m, ok := ctx.Module().(*Module)
-	if !ok || !m.ConvertWithBp2build(ctx) {
-		return
-	}
-
-	// a Module can be something other than a `modType`
-	if ctx.ModuleType() != modType {
-		return
-	}
-
+func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *Module) {
 	// TODO(b/182306917): this doesn't fully handle all nested props versioned
 	// by the python version, which would have been handled by the version split
 	// mutator. This is sufficient for very simple python_library modules under
@@ -81,9 +59,7 @@
 	} else if !py2Enabled && py3Enabled {
 		python_version = &pyVersion3
 	} else if !py2Enabled && !py3Enabled {
-		panic(fmt.Errorf(
-			"error for '%s' module: bp2build's %s converter doesn't understand having "+
-				"neither py2 nor py3 enabled", m.Name(), modType))
+		ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
 	} else {
 		// do nothing, since python_version defaults to PY2ANDPY3
 	}
@@ -96,8 +72,8 @@
 	}
 
 	props := bazel.BazelTargetModuleProperties{
-		// Use the native py_library rule.
-		Rule_class: "py_library",
+		Rule_class:        "py_library",
+		Bzl_load_location: "//build/bazel/rules/python:library.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
diff --git a/python/python.go b/python/python.go
index 401d91f..734ac57 100644
--- a/python/python.go
+++ b/python/python.go
@@ -23,6 +23,7 @@
 	"strings"
 
 	"android/soong/bazel"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -667,18 +668,25 @@
 }
 
 // isPythonLibModule returns whether the given module is a Python library Module or not
-// This is distinguished by the fact that Python libraries are not installable, while other Python
-// modules are.
 func isPythonLibModule(module blueprint.Module) bool {
 	if m, ok := module.(*Module); ok {
-		// Python library has no bootstrapper or installer
-		if m.bootstrapper == nil && m.installer == nil {
-			return true
-		}
+		return m.isLibrary()
 	}
 	return false
 }
 
+// This is distinguished by the fact that Python libraries are not installable, while other Python
+// modules are.
+func (p *Module) isLibrary() bool {
+	// Python library has no bootstrapper or installer
+	return p.bootstrapper == nil && p.installer == nil
+}
+
+func (p *Module) isBinary() bool {
+	_, ok := p.bootstrapper.(*binaryDecorator)
+	return ok
+}
+
 // collectPathsFromTransitiveDeps checks for source/data files for duplicate paths
 // for module and its transitive dependencies and collects list of data/source file
 // zips for transitive dependencies.
@@ -752,6 +760,14 @@
 	return true
 }
 
+func (p *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	if p.isLibrary() {
+		pythonLibBp2Build(ctx, p)
+	} else if p.isBinary() {
+		pythonBinaryBp2Build(ctx, p)
+	}
+}
+
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var String = proptools.String
diff --git a/rust/Android.bp b/rust/Android.bp
index 5e14da8..3fd68e5 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -14,6 +14,7 @@
         "soong-snapshot",
     ],
     srcs: [
+        "afdo.go",
         "androidmk.go",
         "benchmark.go",
         "binary.go",
diff --git a/rust/OWNERS b/rust/OWNERS
index b5b795c..d07ef7e 100644
--- a/rust/OWNERS
+++ b/rust/OWNERS
@@ -2,4 +2,4 @@
 per-file * = chh@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
 
 # Limited owners/reviewers of the allowed list.
-per-file allowed_list.go = chh@google.com, ivanlozano@google.com, jeffv@google.com, jgalenson@google.com, mmaurer@google.com, srhines@google.com
+per-file allowed_list.go = chh@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
diff --git a/rust/afdo.go b/rust/afdo.go
new file mode 100644
index 0000000..996fd7e
--- /dev/null
+++ b/rust/afdo.go
@@ -0,0 +1,48 @@
+// Copyright 2022 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 rust
+
+import (
+	"fmt"
+
+	"android/soong/cc"
+)
+
+const afdoFlagFormat = "-Zprofile-sample-use=%s"
+
+type afdo struct {
+	Properties cc.AfdoProperties
+}
+
+func (afdo *afdo) props() []interface{} {
+	return []interface{}{&afdo.Properties}
+}
+
+func (afdo *afdo) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
+	if ctx.Host() {
+		return flags, deps
+	}
+
+	if afdo != nil && afdo.Properties.Afdo {
+		if profileFile := afdo.Properties.GetAfdoProfileFile(ctx, ctx.ModuleName()); profileFile.Valid() {
+			profileUseFlag := fmt.Sprintf(afdoFlagFormat, profileFile)
+			flags.RustFlags = append(flags.RustFlags, profileUseFlag)
+
+			profileFilePath := profileFile.Path()
+			deps.AfdoProfiles = append(deps.AfdoProfiles, profileFilePath)
+		}
+	}
+	return flags, deps
+}
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 1f18b4a..2361e03 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -106,6 +106,9 @@
 			}
 			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(test.Properties.Auto_gen_config, true))
 			entries.SetBoolIfTrue("LOCAL_IS_UNIT_TEST", Bool(test.Properties.Test_options.Unit_test))
+			if test.Properties.Data_bins != nil {
+				entries.AddStrings("LOCAL_TEST_DATA_BINS", test.Properties.Data_bins...)
+			}
 		})
 
 	cc.AndroidMkWriteTestData(test.data, ret)
@@ -193,7 +196,7 @@
 	ret.ExtraEntries = append(ret.ExtraEntries,
 		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 			entries.SetPath("LOCAL_SOONG_UNSTRIPPED_BINARY", compiler.unstrippedOutputFile)
-			path, file := filepath.Split(compiler.path.ToMakePath().String())
+			path, file := filepath.Split(compiler.path.String())
 			stem, suffix, _ := android.SplitFileExt(file)
 			entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
 			entries.SetString("LOCAL_MODULE_PATH", path)
diff --git a/rust/binary.go b/rust/binary.go
index db91ccb..0dc320e 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -92,14 +92,23 @@
 func (binary *binaryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
 	deps = binary.baseCompiler.compilerDeps(ctx, deps)
 
+	static := Bool(binary.Properties.Static_executable)
 	if ctx.toolchain().Bionic() {
-		deps = bionicDeps(ctx, deps, Bool(binary.Properties.Static_executable))
-		if Bool(binary.Properties.Static_executable) {
-			deps.CrtBegin = "crtbegin_static"
+		deps = bionicDeps(ctx, deps, static)
+		if static {
+			deps.CrtBegin = []string{"crtbegin_static"}
 		} else {
-			deps.CrtBegin = "crtbegin_dynamic"
+			deps.CrtBegin = []string{"crtbegin_dynamic"}
 		}
-		deps.CrtEnd = "crtend_android"
+		deps.CrtEnd = []string{"crtend_android"}
+	} else if ctx.Os() == android.LinuxMusl {
+		deps = muslDeps(ctx, deps, static)
+		if static {
+			deps.CrtBegin = []string{"libc_musl_crtbegin_static"}
+		} else {
+			deps.CrtBegin = []string{"libc_musl_crtbegin_dynamic", "musl_linker_script"}
+		}
+		deps.CrtEnd = []string{"libc_musl_crtend"}
 	}
 
 	return deps
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 32d02e4..f4c337d 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -30,7 +30,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r433403"
+	bindgenClangVersion = "clang-r437112b"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
@@ -288,6 +288,8 @@
 	deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps)
 	if ctx.toolchain().Bionic() {
 		deps = bionicDeps(ctx, deps, false)
+	} else if ctx.Os() == android.LinuxMusl {
+		deps = muslDeps(ctx, deps, false)
 	}
 
 	deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...)
diff --git a/rust/builder.go b/rust/builder.go
index 60b5926..00035b9 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -216,6 +216,13 @@
 	// Suppress an implicit sysroot
 	rustcFlags = append(rustcFlags, "--sysroot=/dev/null")
 
+	// Enable incremental compilation if requested by user
+	if ctx.Config().IsEnvTrue("SOONG_RUSTC_INCREMENTAL") {
+		incrementalPath := android.PathForOutput(ctx, "rustc").String()
+
+		rustcFlags = append(rustcFlags, "-C incremental="+incrementalPath)
+	}
+
 	// Collect linker flags
 	linkFlags = append(linkFlags, flags.GlobalLinkFlags...)
 	linkFlags = append(linkFlags, flags.LinkFlags...)
@@ -238,10 +245,10 @@
 	implicits = append(implicits, deps.StaticLibs...)
 	implicits = append(implicits, deps.SharedLibDeps...)
 	implicits = append(implicits, deps.srcProviderFiles...)
+	implicits = append(implicits, deps.AfdoProfiles...)
 
-	if deps.CrtBegin.Valid() {
-		implicits = append(implicits, deps.CrtBegin.Path(), deps.CrtEnd.Path())
-	}
+	implicits = append(implicits, deps.CrtBegin...)
+	implicits = append(implicits, deps.CrtEnd...)
 
 	if len(deps.SrcDeps) > 0 {
 		moduleGenDir := ctx.RustModule().compiler.CargoOutDir()
@@ -311,8 +318,8 @@
 			"rustcFlags": strings.Join(rustcFlags, " "),
 			"linkFlags":  strings.Join(linkFlags, " "),
 			"libFlags":   strings.Join(libFlags, " "),
-			"crtBegin":   deps.CrtBegin.String(),
-			"crtEnd":     deps.CrtEnd.String(),
+			"crtBegin":   strings.Join(deps.CrtBegin.Strings(), " "),
+			"crtEnd":     strings.Join(deps.CrtEnd.Strings(), " "),
 			"envVars":    strings.Join(envVars, " "),
 		},
 	})
diff --git a/rust/compiler.go b/rust/compiler.go
index 3040e5d..c5d40f4 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -364,7 +364,7 @@
 	if !Bool(compiler.Properties.No_stdlibs) {
 		for _, stdlib := range config.Stdlibs {
 			// If we're building for the build host, use the prebuilt stdlibs
-			if ctx.Target().Os == ctx.Config().BuildOS {
+			if ctx.Target().Os == android.Linux || ctx.Target().Os == android.Darwin {
 				stdlib = "prebuilt_" + stdlib
 			}
 			deps.Stdlibs = append(deps.Stdlibs, stdlib)
@@ -394,6 +394,20 @@
 	return deps
 }
 
+func muslDeps(ctx DepsContext, deps Deps, static bool) Deps {
+	muslLibs := []string{"libc_musl"}
+	if static {
+		deps.StaticLibs = append(deps.StaticLibs, muslLibs...)
+	} else {
+		deps.SharedLibs = append(deps.SharedLibs, muslLibs...)
+	}
+	if libRuntimeBuiltins := config.BuiltinsRuntimeLibrary(ctx.toolchain()); libRuntimeBuiltins != "" {
+		deps.StaticLibs = append(deps.StaticLibs, libRuntimeBuiltins)
+	}
+
+	return deps
+}
+
 func (compiler *baseCompiler) crateName() string {
 	return compiler.Properties.Crate_name
 }
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 0d0b712..14fcb02 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -19,6 +19,7 @@
 		"frameworks/native/libs/binder/rust",
 		"frameworks/proto_logging/stats",
 		"hardware/interfaces/security",
+		"hardware/interfaces/uwb",
 		"packages/modules/Bluetooth",
 		"packages/modules/DnsResolver",
 		"packages/modules/Uwb",
@@ -36,6 +37,8 @@
 		"system/tools/aidl",
 		"tools/security/fuzzing/example_rust_fuzzer",
 		"tools/security/fuzzing/orphans",
+		"tools/security/remote_provisioning/cert_validator",
+		"tools/vendor",
 		"vendor/",
 	}
 
diff --git a/rust/config/global.go b/rust/config/global.go
index acc7a79..c1ce13f 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,9 +24,9 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.56.1p1"
+	RustDefaultVersion = "1.58.1"
 	RustDefaultBase    = "prebuilts/rust/"
-	DefaultEdition     = "2018"
+	DefaultEdition     = "2021"
 	Stdlibs            = []string{
 		"libstd",
 	}
@@ -41,7 +41,7 @@
 	}
 
 	GlobalRustFlags = []string{
-		"--remap-path-prefix $$(pwd)=",
+		"-Z remap-cwd-prefix=.",
 		"-C codegen-units=1",
 		"-C debuginfo=2",
 		"-C opt-level=3",
@@ -55,6 +55,8 @@
 	deviceGlobalRustFlags = []string{
 		"-C panic=abort",
 		"-Z link-native-libraries=no",
+		// Generate additional debug info for AutoFDO
+		"-Z debug-info-for-profiling",
 	}
 
 	deviceGlobalLinkFlags = []string{
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index c10afd8..7608349 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -22,12 +22,29 @@
 
 var (
 	LinuxRustFlags     = []string{}
+	LinuxMuslRustFlags = []string{
+		// disable rustc's builtin fallbacks for crt objects
+		"-C link_self_contained=no",
+		// force rustc to use a dynamic musl libc
+		"-C target-feature=-crt-static",
+		"-Z link-native-libraries=no",
+	}
 	LinuxRustLinkFlags = []string{
 		"-B${cc_config.ClangBin}",
 		"-fuse-ld=lld",
 		"-Wl,--undefined-version",
+	}
+	LinuxRustGlibcLinkFlags = []string{
 		"--sysroot ${cc_config.LinuxGccRoot}/sysroot",
 	}
+	LinuxRustMuslLinkFlags = []string{
+		"--sysroot /dev/null",
+		"-nodefaultlibs",
+		"-nostdlib",
+		"-Wl,--no-dynamic-linker",
+		// for unwind
+		"-lgcc", "-lgcc_eh",
+	}
 	linuxX86Rustflags   = []string{}
 	linuxX86Linkflags   = []string{}
 	linuxX8664Rustflags = []string{}
@@ -35,15 +52,17 @@
 )
 
 func init() {
-	registerToolchainFactory(android.Linux, android.X86_64, linuxX8664ToolchainFactory)
-	registerToolchainFactory(android.Linux, android.X86, linuxX86ToolchainFactory)
+	registerToolchainFactory(android.Linux, android.X86_64, linuxGlibcX8664ToolchainFactory)
+	registerToolchainFactory(android.Linux, android.X86, linuxGlibcX86ToolchainFactory)
 
-	// TODO: musl rust support
-	registerToolchainFactory(android.LinuxMusl, android.X86_64, linuxX8664ToolchainFactory)
-	registerToolchainFactory(android.LinuxMusl, android.X86, linuxX86ToolchainFactory)
+	registerToolchainFactory(android.LinuxMusl, android.X86_64, linuxMuslX8664ToolchainFactory)
+	registerToolchainFactory(android.LinuxMusl, android.X86, linuxMuslX86ToolchainFactory)
 
 	pctx.StaticVariable("LinuxToolchainRustFlags", strings.Join(LinuxRustFlags, " "))
+	pctx.StaticVariable("LinuxMuslToolchainRustFlags", strings.Join(LinuxMuslRustFlags, " "))
 	pctx.StaticVariable("LinuxToolchainLinkFlags", strings.Join(LinuxRustLinkFlags, " "))
+	pctx.StaticVariable("LinuxGlibcToolchainLinkFlags", strings.Join(LinuxRustGlibcLinkFlags, " "))
+	pctx.StaticVariable("LinuxMuslToolchainLinkFlags", strings.Join(LinuxRustMuslLinkFlags, " "))
 	pctx.StaticVariable("LinuxToolchainX86RustFlags", strings.Join(linuxX86Rustflags, " "))
 	pctx.StaticVariable("LinuxToolchainX86LinkFlags", strings.Join(linuxX86Linkflags, " "))
 	pctx.StaticVariable("LinuxToolchainX8664RustFlags", strings.Join(linuxX8664Rustflags, " "))
@@ -51,19 +70,9 @@
 
 }
 
-type toolchainLinux struct {
-	toolchainRustFlags string
-	toolchainLinkFlags string
-}
-
-type toolchainLinuxX86 struct {
-	toolchain32Bit
-	toolchainLinux
-}
-
+// Base 64-bit linux rust toolchain
 type toolchainLinuxX8664 struct {
 	toolchain64Bit
-	toolchainLinux
 }
 
 func (toolchainLinuxX8664) Supported() bool {
@@ -78,10 +87,6 @@
 	return "x86_64"
 }
 
-func (t *toolchainLinuxX8664) RustTriple() string {
-	return "x86_64-unknown-linux-gnu"
-}
-
 func (t *toolchainLinuxX8664) ToolchainLinkFlags() string {
 	// Prepend the lld flags from cc_config so we stay in sync with cc
 	return "${cc_config.LinuxLldflags} ${cc_config.LinuxX8664Lldflags} " +
@@ -92,8 +97,49 @@
 	return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainX8664RustFlags}"
 }
 
-func linuxX8664ToolchainFactory(arch android.Arch) Toolchain {
-	return toolchainLinuxX8664Singleton
+// Specialization of the 64-bit linux rust toolchain for glibc.  Adds the gnu rust triple and
+// sysroot linker flags.
+type toolchainLinuxGlibcX8664 struct {
+	toolchainLinuxX8664
+}
+
+func (t *toolchainLinuxX8664) RustTriple() string {
+	return "x86_64-unknown-linux-gnu"
+}
+
+func (t *toolchainLinuxGlibcX8664) ToolchainLinkFlags() string {
+	return t.toolchainLinuxX8664.ToolchainLinkFlags() + " " + "${config.LinuxGlibcToolchainLinkFlags}"
+}
+
+func linuxGlibcX8664ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxGlibcX8664Singleton
+}
+
+// Specialization of the 64-bit linux rust toolchain for musl.  Adds the musl rust triple and
+// linker flags to avoid using the host sysroot.
+type toolchainLinuxMuslX8664 struct {
+	toolchainLinuxX8664
+}
+
+func (t *toolchainLinuxMuslX8664) RustTriple() string {
+	return "x86_64-unknown-linux-musl"
+}
+
+func (t *toolchainLinuxMuslX8664) ToolchainLinkFlags() string {
+	return t.toolchainLinuxX8664.ToolchainLinkFlags() + " " + "${config.LinuxMuslToolchainLinkFlags}"
+}
+
+func (t *toolchainLinuxMuslX8664) ToolchainRustFlags() string {
+	return t.toolchainLinuxX8664.ToolchainRustFlags() + " " + "${config.LinuxMuslToolchainRustFlags}"
+}
+
+func linuxMuslX8664ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxMuslX8664Singleton
+}
+
+// Base 32-bit linux rust toolchain
+type toolchainLinuxX86 struct {
+	toolchain32Bit
 }
 
 func (toolchainLinuxX86) Supported() bool {
@@ -116,10 +162,6 @@
 	return "x86_64"
 }
 
-func (t *toolchainLinuxX86) RustTriple() string {
-	return "i686-unknown-linux-gnu"
-}
-
 func (t *toolchainLinuxX86) ToolchainLinkFlags() string {
 	// Prepend the lld flags from cc_config so we stay in sync with cc
 	return "${cc_config.LinuxLldflags} ${cc_config.LinuxX86Lldflags} " +
@@ -130,9 +172,47 @@
 	return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainX86RustFlags}"
 }
 
-func linuxX86ToolchainFactory(arch android.Arch) Toolchain {
-	return toolchainLinuxX86Singleton
+// Specialization of the 32-bit linux rust toolchain for glibc.  Adds the gnu rust triple and
+// sysroot linker flags.
+type toolchainLinuxGlibcX86 struct {
+	toolchainLinuxX86
 }
 
-var toolchainLinuxX8664Singleton Toolchain = &toolchainLinuxX8664{}
-var toolchainLinuxX86Singleton Toolchain = &toolchainLinuxX86{}
+func (t *toolchainLinuxGlibcX86) RustTriple() string {
+	return "i686-unknown-linux-gnu"
+}
+
+func (t *toolchainLinuxGlibcX86) ToolchainLinkFlags() string {
+	return t.toolchainLinuxX86.ToolchainLinkFlags() + " " + "${config.LinuxGlibcToolchainLinkFlags}"
+}
+
+func linuxGlibcX86ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxGlibcX86Singleton
+}
+
+// Specialization of the 32-bit linux rust toolchain for musl.  Adds the musl rust triple and
+// linker flags to avoid using the host sysroot.
+type toolchainLinuxMuslX86 struct {
+	toolchainLinuxX86
+}
+
+func (t *toolchainLinuxMuslX86) RustTriple() string {
+	return "i686-unknown-linux-musl"
+}
+
+func (t *toolchainLinuxMuslX86) ToolchainLinkFlags() string {
+	return t.toolchainLinuxX86.ToolchainLinkFlags() + " " + "${config.LinuxMuslToolchainLinkFlags}"
+}
+
+func (t *toolchainLinuxMuslX86) ToolchainRustFlags() string {
+	return t.toolchainLinuxX86.ToolchainRustFlags() + " " + "${config.LinuxMuslToolchainRustFlags}"
+}
+
+func linuxMuslX86ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxMuslX86Singleton
+}
+
+var toolchainLinuxGlibcX8664Singleton Toolchain = &toolchainLinuxGlibcX8664{}
+var toolchainLinuxGlibcX86Singleton Toolchain = &toolchainLinuxGlibcX86{}
+var toolchainLinuxMuslX8664Singleton Toolchain = &toolchainLinuxMuslX8664{}
+var toolchainLinuxMuslX86Singleton Toolchain = &toolchainLinuxMuslX86{}
diff --git a/rust/coverage.go b/rust/coverage.go
index 8fdfa23..050b811 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -57,18 +57,7 @@
 		flags.RustFlags = append(flags.RustFlags,
 			"-Z instrument-coverage", "-g")
 		flags.LinkFlags = append(flags.LinkFlags,
-			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",
-		)
+			profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
 		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
 	}
 
diff --git a/rust/library.go b/rust/library.go
index 07843fe..62eaefd 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -426,10 +426,16 @@
 func (library *libraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
 	deps = library.baseCompiler.compilerDeps(ctx, deps)
 
-	if ctx.toolchain().Bionic() && (library.dylib() || library.shared()) {
-		deps = bionicDeps(ctx, deps, false)
-		deps.CrtBegin = "crtbegin_so"
-		deps.CrtEnd = "crtend_so"
+	if library.dylib() || library.shared() {
+		if ctx.toolchain().Bionic() {
+			deps = bionicDeps(ctx, deps, false)
+			deps.CrtBegin = []string{"crtbegin_so"}
+			deps.CrtEnd = []string{"crtend_so"}
+		} else if ctx.Os() == android.LinuxMusl {
+			deps = muslDeps(ctx, deps, false)
+			deps.CrtBegin = []string{"libc_musl_crtbegin_so"}
+			deps.CrtEnd = []string{"libc_musl_crtend_so"}
+		}
 	}
 
 	return deps
@@ -681,6 +687,12 @@
 			}
 
 			variation := v.(*Module).ModuleBase.ImageVariation().Variation
+			if strings.HasPrefix(variation, cc.VendorVariationPrefix) {
+				// TODO(b/204303985)
+				// Disable vendor dylibs until they are supported
+				v.(*Module).Disable()
+			}
+
 			if strings.HasPrefix(variation, cc.VendorVariationPrefix) &&
 				m.HasVendorVariant() &&
 				!snapshot.IsVendorProprietaryModule(mctx) &&
@@ -761,9 +773,10 @@
 	// Glob together the headers from the modules include_dirs property
 	for _, path := range android.CopyOfPaths(l.includeDirs) {
 		dir := path.String()
-		glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
+		globDir := dir + "/**/*"
+		glob, err := ctx.GlobWithDeps(globDir, nil)
 		if err != nil {
-			ctx.ModuleErrorf("glob failed: %#v", err)
+			ctx.ModuleErrorf("glob of %q failed: %s", globDir, err)
 			return
 		}
 
diff --git a/rust/project_json.go b/rust/project_json.go
index ae48312..fe259d6 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -211,6 +211,8 @@
 		comp = c.binaryDecorator.baseCompiler
 	case *procMacroDecorator:
 		comp = c.baseCompiler
+	case *toolchainLibraryDecorator:
+		comp = c.baseCompiler
 	default:
 		return nil, nil, false
 	}
diff --git a/rust/protobuf.go b/rust/protobuf.go
index b91fea8..9fe27c4c 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -188,6 +188,12 @@
 			lines,
 			"pub mod empty {",
 			"    pub use protobuf::well_known_types::Empty;",
+			"}",
+			"pub mod wrappers {",
+			"    pub use protobuf::well_known_types::{",
+			"        DoubleValue, FloatValue, Int64Value, UInt64Value, Int32Value, UInt32Value,",
+			"        BoolValue, StringValue, BytesValue",
+			"    };",
 			"}")
 	}
 
diff --git a/rust/rust.go b/rust/rust.go
index b575c7a..f40f1a8 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -148,6 +148,7 @@
 
 	makeLinkType string
 
+	afdo             *afdo
 	compiler         compiler
 	coverage         *coverage
 	clippy           *clippy
@@ -366,10 +367,6 @@
 	return ""
 }
 
-func (mod *Module) MinSdkVersion() string {
-	return ""
-}
-
 func (mod *Module) AlwaysSdk() bool {
 	return false
 }
@@ -397,7 +394,7 @@
 	DataLibs []string
 	DataBins []string
 
-	CrtBegin, CrtEnd string
+	CrtBegin, CrtEnd []string
 }
 
 type PathDeps struct {
@@ -407,6 +404,7 @@
 	SharedLibDeps android.Paths
 	StaticLibs    android.Paths
 	ProcMacros    RustLibraries
+	AfdoProfiles  android.Paths
 
 	// depFlags and depLinkFlags are rustc and linker (clang) flags.
 	depFlags     []string
@@ -423,8 +421,8 @@
 	depGeneratedHeaders   android.Paths
 	depSystemIncludePaths android.Paths
 
-	CrtBegin android.OptionalPath
-	CrtEnd   android.OptionalPath
+	CrtBegin android.Paths
+	CrtEnd   android.Paths
 
 	// Paths to generated source files
 	SrcDeps          android.Paths
@@ -555,6 +553,7 @@
 	module.AddProperties(props...)
 	module.AddProperties(
 		&BaseProperties{},
+		&cc.AfdoProperties{},
 		&cc.VendorProperties{},
 		&BenchmarkProperties{},
 		&BindgenProperties{},
@@ -692,6 +691,9 @@
 	mod.AddProperties(&mod.Properties)
 	mod.AddProperties(&mod.VendorProperties)
 
+	if mod.afdo != nil {
+		mod.AddProperties(mod.afdo.props()...)
+	}
 	if mod.compiler != nil {
 		mod.AddProperties(mod.compiler.compilerProps()...)
 	}
@@ -723,6 +725,7 @@
 }
 func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
 	module := newBaseModule(hod, multilib)
+	module.afdo = &afdo{}
 	module.coverage = &coverage{}
 	module.clippy = &clippy{}
 	module.sanitize = &sanitize{}
@@ -860,6 +863,9 @@
 	}
 
 	// Calculate rustc flags
+	if mod.afdo != nil {
+		flags, deps = mod.afdo.flags(ctx, flags, deps)
+	}
 	if mod.compiler != nil {
 		flags = mod.compiler.compilerFlags(ctx, flags)
 		flags = mod.compiler.cfgFlags(ctx, flags)
@@ -901,6 +907,9 @@
 		bloaty.MeasureSizeForPaths(ctx, mod.compiler.strippedOutputFilePath(), android.OptionalPathForPath(mod.compiler.unstrippedOutputFilePath()))
 
 		mod.docTimestampFile = mod.compiler.rustdoc(ctx, flags, deps)
+		if mod.docTimestampFile.Valid() {
+			ctx.CheckbuildFile(mod.docTimestampFile.Path())
+		}
 
 		// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
 		// RECOVERY_SNAPSHOT_VERSION is current.
@@ -969,6 +978,7 @@
 	name      string
 	library   bool
 	procMacro bool
+	dynamic   bool
 }
 
 // InstallDepNeeded returns true for rlibs, dylibs, and proc macros so that they or their transitive
@@ -979,10 +989,19 @@
 
 var _ android.InstallNeededDependencyTag = dependencyTag{}
 
+func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
+	if d.library && d.dynamic {
+		return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency}
+	}
+	return nil
+}
+
+var _ android.LicenseAnnotationsDependencyTag = dependencyTag{}
+
 var (
 	customBindgenDepTag = dependencyTag{name: "customBindgenTag"}
 	rlibDepTag          = dependencyTag{name: "rlibTag", library: true}
-	dylibDepTag         = dependencyTag{name: "dylib", library: true}
+	dylibDepTag         = dependencyTag{name: "dylib", library: true, dynamic: true}
 	procMacroDepTag     = dependencyTag{name: "procMacro", procMacro: true}
 	testPerSrcDepTag    = dependencyTag{name: "rust_unit_tests"}
 	sourceDepTag        = dependencyTag{name: "source"}
@@ -1173,6 +1192,10 @@
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
 				directStaticLibDeps = append(directStaticLibDeps, ccDep)
+
+				// Record baseLibName for snapshots.
+				mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
+
 				mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, makeLibName)
 			case cc.IsSharedDepTag(depTag):
 				// For the shared lib dependencies, we may link to the stub variant
@@ -1195,7 +1218,6 @@
 
 				// Record baseLibName for snapshots.
 				mod.Properties.SnapshotSharedLibs = append(mod.Properties.SnapshotSharedLibs, cc.BaseLibName(depName))
-				mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
 
 				mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName)
 				exportDep = true
@@ -1205,9 +1227,9 @@
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
 			case depTag == cc.CrtBeginDepTag:
-				depPaths.CrtBegin = linkObject
+				depPaths.CrtBegin = append(depPaths.CrtBegin, linkObject.Path())
 			case depTag == cc.CrtEndDepTag:
-				depPaths.CrtEnd = linkObject
+				depPaths.CrtEnd = append(depPaths.CrtEnd, linkObject.Path())
 			}
 
 			// Make sure these dependencies are propagated
@@ -1215,6 +1237,13 @@
 				lib.exportLinkDirs(linkPath)
 				lib.exportLinkObjects(linkObject.String())
 			}
+		} else {
+			switch {
+			case depTag == cc.CrtBeginDepTag:
+				depPaths.CrtBegin = append(depPaths.CrtBegin, android.OutputFileForModule(ctx, dep, ""))
+			case depTag == cc.CrtEndDepTag:
+				depPaths.CrtEnd = append(depPaths.CrtEnd, android.OutputFileForModule(ctx, dep, ""))
+			}
 		}
 
 		if srcDep, ok := dep.(android.SourceFileProducer); ok {
@@ -1302,10 +1331,6 @@
 	return mod.InRecovery()
 }
 
-func (mod *Module) InstallBypassMake() bool {
-	return true
-}
-
 func linkPathFromFilePath(filepath android.Path) string {
 	return strings.Split(filepath.String(), filepath.Base())[0]
 }
@@ -1417,13 +1442,13 @@
 	actx.AddVariationDependencies(nil, cc.HeaderDepTag(), deps.HeaderLibs...)
 
 	crtVariations := cc.GetCrtVariations(ctx, mod)
-	if deps.CrtBegin != "" {
+	for _, crt := range deps.CrtBegin {
 		actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag,
-			cc.RewriteSnapshotLib(deps.CrtBegin, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
+			cc.RewriteSnapshotLib(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
 	}
-	if deps.CrtEnd != "" {
+	for _, crt := range deps.CrtEnd {
 		actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag,
-			cc.RewriteSnapshotLib(deps.CrtEnd, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
+			cc.RewriteSnapshotLib(crt, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
 	}
 
 	if mod.sourceProvider != nil {
@@ -1488,15 +1513,13 @@
 
 var _ android.ApexModule = (*Module)(nil)
 
-func (mod *Module) minSdkVersion() string {
+func (mod *Module) MinSdkVersion() string {
 	return String(mod.Properties.Min_sdk_version)
 }
 
-var _ android.ApexModule = (*Module)(nil)
-
 // Implements android.ApexModule
 func (mod *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
-	minSdkVersion := mod.minSdkVersion()
+	minSdkVersion := mod.MinSdkVersion()
 	if minSdkVersion == "apex_inherit" {
 		return nil
 	}
diff --git a/rust/test.go b/rust/test.go
index 3eea0ad..250b765 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -210,6 +210,12 @@
 
 func RustTestFactory() android.Module {
 	module, _ := NewRustTest(android.HostAndDeviceSupported)
+
+	// NewRustTest will set MultilibBoth true, however the host variant
+	// cannot produce the non-primary target. Therefore, add the
+	// rustTestHostMultilib load hook to set MultilibFirst for the
+	// host target.
+	android.AddLoadHook(module, rustTestHostMultilib)
 	return module.Init()
 }
 
@@ -236,3 +242,16 @@
 func (test *testDecorator) testBinary() bool {
 	return true
 }
+
+func rustTestHostMultilib(ctx android.LoadHookContext) {
+	type props struct {
+		Target struct {
+			Host struct {
+				Compile_multilib *string
+			}
+		}
+	}
+	p := &props{}
+	p.Target.Host.Compile_multilib = proptools.StringPtr("first")
+	ctx.AppendProperties(p)
+}
diff --git a/scripts/OWNERS b/scripts/OWNERS
index 88787cd..3f4f9c0 100644
--- a/scripts/OWNERS
+++ b/scripts/OWNERS
@@ -1,5 +1,4 @@
 per-file system-clang-format,system-clang-format-2 = enh@google.com,smoreland@google.com
-per-file build-mainline-modules.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
 per-file build-aml-prebuilts.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
 per-file construct_context.py = ngeoffray@google.com,calin@google.com,skvadrik@google.com
 per-file conv_linker_config.py = kiyoungkim@google.com, jiyong@google.com, jooyung@google.com
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
deleted file mode 100755
index 1e3f6ce..0000000
--- a/scripts/build-mainline-modules.sh
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/bin/bash -e
-
-# Non exhaustive list of modules where we want prebuilts. More can be added as
-# needed.
-MAINLINE_MODULES=(
-  com.android.art
-  com.android.art.debug
-  com.android.art.testing
-  com.android.conscrypt
-  com.android.i18n
-  com.android.os.statsd
-  com.android.runtime
-  com.android.tzdata
-)
-
-# List of SDKs and module exports we know of.
-MODULES_SDK_AND_EXPORTS=(
-  art-module-sdk
-  art-module-test-exports
-  conscrypt-module-host-exports
-  conscrypt-module-sdk
-  conscrypt-module-test-exports
-  i18n-module-host-exports
-  i18n-module-sdk
-  i18n-module-test-exports
-  platform-mainline-sdk
-  platform-mainline-test-exports
-  runtime-module-host-exports
-  runtime-module-sdk
-  statsd-module-sdk
-  tzdata-module-test-exports
-)
-
-# List of libraries installed on the platform that are needed for ART chroot
-# testing.
-PLATFORM_LIBRARIES=(
-  heapprofd_client_api
-  libartpalette-system
-  liblog
-)
-
-# We want to create apex modules for all supported architectures.
-PRODUCTS=(
-  aosp_arm
-  aosp_arm64
-  aosp_x86
-  aosp_x86_64
-)
-
-if [ ! -e "build/make/core/Makefile" ]; then
-  echo "$0 must be run from the top of the tree"
-  exit 1
-fi
-
-echo_and_run() {
-  echo "$*"
-  "$@"
-}
-
-lib_dir() {
-  case $1 in
-    (aosp_arm|aosp_x86) echo "lib";;
-    (aosp_arm64|aosp_x86_64) echo "lib64";;
-  esac
-}
-
-# Make sure this build builds from source, regardless of the default.
-export SOONG_CONFIG_art_module_source_build=true
-
-# This script does not intend to handle compressed APEX
-export OVERRIDE_PRODUCT_COMPRESSED_APEX=false
-
-OUT_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var OUT_DIR)
-DIST_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var DIST_DIR)
-
-for product in "${PRODUCTS[@]}"; do
-  echo_and_run build/soong/soong_ui.bash --make-mode $@ \
-    TARGET_PRODUCT=${product} \
-    ${MAINLINE_MODULES[@]} \
-    ${PLATFORM_LIBRARIES[@]}
-
-  PRODUCT_OUT=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var PRODUCT_OUT)
-  TARGET_ARCH=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT=${product} get_build_var TARGET_ARCH)
-  rm -rf ${DIST_DIR}/${TARGET_ARCH}/
-  mkdir -p ${DIST_DIR}/${TARGET_ARCH}/
-  for module in "${MAINLINE_MODULES[@]}"; do
-    echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/apex/${module}.apex ${DIST_DIR}/${TARGET_ARCH}/
-  done
-  for library in "${PLATFORM_LIBRARIES[@]}"; do
-    libdir=$(lib_dir $product)
-    echo_and_run cp ${PWD}/${PRODUCT_OUT}/system/${libdir}/${library}.so ${DIST_DIR}/${TARGET_ARCH}/
-  done
-done
-
-# We use force building LLVM components flag (even though we actually don't
-# compile them) because we don't have bionic host prebuilts
-# for them.
-export FORCE_BUILD_LLVM_COMPONENTS=true
-
-# Create multi-archs SDKs in a different out directory. The multi-arch script
-# uses Soong in --soong-only mode which cannot use the same directory as normal
-# mode with make.
-export OUT_DIR=${OUT_DIR}/aml
-echo_and_run build/soong/scripts/build-aml-prebuilts.sh \
-  TARGET_PRODUCT=mainline_sdk ${MODULES_SDK_AND_EXPORTS[@]}
-
-rm -rf ${DIST_DIR}/mainline-sdks
-echo_and_run cp -R ${OUT_DIR}/soong/mainline-sdks ${DIST_DIR}
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
index ed63651..a02c195 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -72,6 +72,7 @@
 jdk\.internal
 jdk\.internal\.math
 jdk\.internal\.misc
+jdk\.internal\.ref
 jdk\.internal\.reflect
 jdk\.internal\.util
 jdk\.internal\.vm\.annotation
diff --git a/scripts/gen_java_usedby_apex.sh b/scripts/gen_java_usedby_apex.sh
index 251d7aa..e398541 100755
--- a/scripts/gen_java_usedby_apex.sh
+++ b/scripts/gen_java_usedby_apex.sh
@@ -30,9 +30,11 @@
   shift
   rm -f "$out"
   touch "$out"
+  echo "<externals>" >> "$out"
   for x in "$@"; do
-    "$dexdeps" "$x" >> "$out" || true
+    "$dexdeps" "$x" >> "$out" || echo "</external>" >> "$out"
   done
+  echo "</externals>" >> "$out"
 }
 
 if [[ "$1" == "help" ]]
diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py
index d80a617..2d3103b 100755
--- a/scripts/manifest_fixer.py
+++ b/scripts/manifest_fixer.py
@@ -65,6 +65,9 @@
   parser.add_argument('--has-no-code', dest='has_no_code', action='store_true',
                       help=('adds hasCode="false" attribute to application. Ignored if application elem '
                             'already has a hasCode attribute.'))
+  parser.add_argument('--test-only', dest='test_only', action='store_true',
+                      help=('adds testOnly="true" attribute to application. Assign true value if application elem '
+                            'already has a testOnly attribute.'))
   parser.add_argument('input', help='input AndroidManifest.xml file')
   parser.add_argument('output', help='output AndroidManifest.xml file')
   return parser.parse_args()
@@ -318,6 +321,26 @@
   attr.value = 'false'
   application.setAttributeNode(attr)
 
+def set_test_only_flag_to_true(doc):
+  manifest = parse_manifest(doc)
+  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:
+    application = doc.createElement('application')
+    indent = get_indent(manifest.firstChild, 1)
+    first = manifest.firstChild
+    manifest.insertBefore(doc.createTextNode(indent), first)
+    manifest.insertBefore(application, first)
+
+  attr = application.getAttributeNodeNS(android_ns, 'testOnly')
+  if attr is not None:
+    # Do nothing If the application already has a testOnly attribute.
+    return
+  attr = doc.createAttributeNS(android_ns, 'android:testOnly')
+  attr.value = 'true'
+  application.setAttributeNode(attr)
 
 def main():
   """Program entry point."""
@@ -349,6 +372,9 @@
     if args.has_no_code:
       set_has_code_to_false(doc)
 
+    if args.test_only:
+      set_test_only_flag_to_true(doc)
+
     if args.extract_native_libs is not None:
       add_extract_native_libs(doc, args.extract_native_libs)
 
diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py
index f6fcaaf..199b279 100755
--- a/scripts/manifest_fixer_test.py
+++ b/scripts/manifest_fixer_test.py
@@ -521,12 +521,55 @@
     self.assert_xml_equal(output, manifest_input)
 
   def test_has_application_has_code_true(self):
-    """ Do nothing if there's already an application elemeent even if its
+    """ Do nothing if there's already an application element even if its
      hasCode attribute is true. """
     manifest_input = self.manifest_tmpl % '    <application android:hasCode="true"/>\n'
     output = self.run_test(manifest_input)
     self.assert_xml_equal(output, manifest_input)
 
 
+class AddTestOnlyApplicationTest(unittest.TestCase):
+  """Unit tests for set_test_only_flag_to_true function."""
+
+  def assert_xml_equal(self, output, expected):
+    self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected))
+
+  def run_test(self, input_manifest):
+    doc = minidom.parseString(input_manifest)
+    manifest_fixer.set_test_only_flag_to_true(doc)
+    output = io.StringIO()
+    manifest_fixer.write_xml(output, doc)
+    return output.getvalue()
+
+  manifest_tmpl = (
+      '<?xml version="1.0" encoding="utf-8"?>\n'
+      '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
+      '%s'
+      '</manifest>\n')
+
+  def test_no_application(self):
+    manifest_input = self.manifest_tmpl % ''
+    expected = self.manifest_tmpl % '    <application android:testOnly="true"/>\n'
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, expected)
+
+  def test_has_application_no_test_only(self):
+    manifest_input = self.manifest_tmpl % '    <application/>\n'
+    expected = self.manifest_tmpl % '    <application android:testOnly="true"/>\n'
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, expected)
+
+  def test_has_application_test_only_true(self):
+    """ If there's already an application element."""
+    manifest_input = self.manifest_tmpl % '    <application android:testOnly="true"/>\n'
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, manifest_input)
+
+  def test_has_application_test_only_false(self):
+    """ If there's already an application element with the testOnly attribute as false."""
+    manifest_input = self.manifest_tmpl % '    <application android:testOnly="false"/>\n'
+    output = self.run_test(manifest_input)
+    self.assert_xml_equal(output, manifest_input)
+
 if __name__ == '__main__':
   unittest.main(verbosity=2)
diff --git a/scripts/rbc-run b/scripts/rbc-run
index 235da75..b8a6c0c 100755
--- a/scripts/rbc-run
+++ b/scripts/rbc-run
@@ -2,15 +2,17 @@
 # Convert and run one configuration
 # Args: a product/board makefile optionally followed by additional arguments
 #       that will be passed to rbcrun.
-[[ $# -gt 0 && -f "$1" ]] || { echo "Usage: ${0##*/} product.mk [Additional rbcrun arguments]" >&2; exit 1; }
+[[ $# -gt 1 && -f "$1" && -f "$2" ]] || { echo "Usage: ${0##*/} product.mk input_variables.mk [Additional rbcrun arguments]" >&2; exit 1; }
 set -eu
 
 declare -r output_root="${OUT_DIR:-out}"
 declare -r runner="${output_root}/soong/rbcrun"
 declare -r converter="${output_root}/soong/mk2rbc"
 declare -r launcher="${output_root}/rbc/launcher.rbc"
+declare -r makefile_list="${output_root}/.module_paths/configuration.list"
 declare -r makefile="$1"
-shift
-"${converter}" -mode=write -r --outdir "${output_root}/rbc" --launcher="${launcher}" "${makefile}"
+declare -r input_variables="$2"
+shift 2
+"${converter}" -mode=write -r --outdir "${output_root}/rbc" --input_variables "${input_variables}" --launcher="${launcher}" --makefile_list="${makefile_list}" "${makefile}"
 "${runner}" RBC_OUT="make,global" RBC_DEBUG="${RBC_DEBUG:-}" $@ "${launcher}"
 
diff --git a/scripts/rustfmt.toml b/scripts/rustfmt.toml
index 617d425..cefaa42 100644
--- a/scripts/rustfmt.toml
+++ b/scripts/rustfmt.toml
@@ -1,5 +1,5 @@
 # Android Format Style
 
-edition = "2018"
+edition = "2021"
 use_small_heuristics = "Max"
 newline_style = "Unix"
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index ff2af43..2dacdb5 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -40,6 +40,7 @@
 			}
 		`, apex, fragment)),
 		android.FixtureAddFile("frameworks/base/config/boot-profile.txt", nil),
+		android.FixtureAddFile("frameworks/base/config/boot-image-profile.txt", nil),
 		android.FixtureAddFile("build/soong/scripts/check_boot_jars/package_allowed_list.txt", nil),
 	)
 }
diff --git a/sdk/build_release.go b/sdk/build_release.go
index a3f0899..4c2277e 100644
--- a/sdk/build_release.go
+++ b/sdk/build_release.go
@@ -85,7 +85,7 @@
 
 	// Add the build releases from oldest to newest.
 	buildReleaseS = initBuildRelease("S")
-	buildReleaseT = initBuildRelease("T")
+	buildReleaseT = initBuildRelease("Tiramisu")
 )
 
 // initBuildRelease creates a new build release with the specified name.
@@ -230,51 +230,108 @@
 			return container.Field(fieldIndex)
 		}
 
-		zeroValue := reflect.Zero(field.Type)
-		fieldPruner := func(container reflect.Value) {
-			if containingStructAccessor != nil {
-				// This is an embedded structure so first access the field for the embedded
-				// structure.
-				container = containingStructAccessor(container)
+		fieldType := field.Type
+		if selector(name, field) {
+			zeroValue := reflect.Zero(fieldType)
+			fieldPruner := func(container reflect.Value) {
+				if containingStructAccessor != nil {
+					// This is an embedded structure so first access the field for the embedded
+					// structure.
+					container = containingStructAccessor(container)
+				}
+
+				// Skip through interface and pointer values to find the structure.
+				container = getStructValue(container)
+
+				defer func() {
+					if r := recover(); r != nil {
+						panic(fmt.Errorf("%s\n\tfor field (index %d, name %s)", r, fieldIndex, name))
+					}
+				}()
+
+				// Set the field.
+				container.Field(fieldIndex).Set(zeroValue)
 			}
 
-			// Skip through interface and pointer values to find the structure.
-			container = getStructValue(container)
-
-			defer func() {
-				if r := recover(); r != nil {
-					panic(fmt.Errorf("%s for fieldIndex %d of field %s of container %#v", r, fieldIndex, name, container.Interface()))
-				}
-			}()
-
-			// Set the field.
-			container.Field(fieldIndex).Set(zeroValue)
-		}
-
-		if selector(name, field) {
 			property := prunerProperty{
 				name,
 				fieldPruner,
 			}
 			p.properties = append(p.properties, property)
-		} else if field.Type.Kind() == reflect.Struct {
-			// Gather fields from the nested or embedded structure.
-			var subNamePrefix string
-			if field.Anonymous {
-				subNamePrefix = namePrefix
-			} else {
-				subNamePrefix = name + "."
+		} else {
+			switch fieldType.Kind() {
+			case reflect.Struct:
+				// Gather fields from the nested or embedded structure.
+				var subNamePrefix string
+				if field.Anonymous {
+					subNamePrefix = namePrefix
+				} else {
+					subNamePrefix = name + "."
+				}
+				p.gatherFields(fieldType, fieldGetter, subNamePrefix, selector)
+
+			case reflect.Map:
+				// Get the type of the values stored in the map.
+				valueType := fieldType.Elem()
+				// Skip over * types.
+				if valueType.Kind() == reflect.Ptr {
+					valueType = valueType.Elem()
+				}
+				if valueType.Kind() == reflect.Struct {
+					// If this is not referenced by a pointer then it is an error as it is impossible to
+					// modify a struct that is stored directly as a value in a map.
+					if fieldType.Elem().Kind() != reflect.Ptr {
+						panic(fmt.Errorf("Cannot prune struct %s stored by value in map %s, map values must"+
+							" be pointers to structs",
+							fieldType.Elem(), name))
+					}
+
+					// Create a new pruner for the values of the map.
+					valuePruner := newPropertyPrunerForStructType(valueType, selector)
+
+					// Create a new fieldPruner that will iterate over all the items in the map and call the
+					// pruner on them.
+					fieldPruner := func(container reflect.Value) {
+						mapValue := fieldGetter(container)
+
+						for _, keyValue := range mapValue.MapKeys() {
+							itemValue := mapValue.MapIndex(keyValue)
+
+							defer func() {
+								if r := recover(); r != nil {
+									panic(fmt.Errorf("%s\n\tfor key %q", r, keyValue))
+								}
+							}()
+
+							valuePruner.pruneProperties(itemValue.Interface())
+						}
+					}
+
+					// Add the map field pruner to the list of property pruners.
+					property := prunerProperty{
+						name + "[*]",
+						fieldPruner,
+					}
+					p.properties = append(p.properties, property)
+				}
 			}
-			p.gatherFields(field.Type, fieldGetter, subNamePrefix, selector)
 		}
 	}
 }
 
-// pruneProperties will prune (set to zero value) any properties in the supplied struct.
+// pruneProperties will prune (set to zero value) any properties in the struct referenced by the
+// supplied struct pointer.
 //
 // The struct must be of the same type as was originally passed to newPropertyPruner to create this
 // propertyPruner.
 func (p *propertyPruner) pruneProperties(propertiesStruct interface{}) {
+
+	defer func() {
+		if r := recover(); r != nil {
+			panic(fmt.Errorf("%s\n\tof container %#v", r, propertiesStruct))
+		}
+	}()
+
 	structValue := reflect.ValueOf(propertiesStruct)
 	for _, property := range p.properties {
 		property.prunerFunc(structValue)
@@ -292,6 +349,13 @@
 // of properties.
 func newPropertyPruner(propertiesStruct interface{}, selector fieldSelectorFunc) *propertyPruner {
 	structType := getStructValue(reflect.ValueOf(propertiesStruct)).Type()
+	return newPropertyPrunerForStructType(structType, selector)
+}
+
+// newPropertyPruner creates a new property pruner for the supplied properties struct type.
+//
+// The returned pruner can be used on any properties structure of the supplied type.
+func newPropertyPrunerForStructType(structType reflect.Type, selector fieldSelectorFunc) *propertyPruner {
 	pruner := &propertyPruner{}
 	pruner.gatherFields(structType, nil, "", selector)
 	return pruner
diff --git a/sdk/build_release_test.go b/sdk/build_release_test.go
index dff276d..6f1ef9e 100644
--- a/sdk/build_release_test.go
+++ b/sdk/build_release_test.go
@@ -15,6 +15,7 @@
 package sdk
 
 import (
+	"encoding/json"
 	"fmt"
 	"testing"
 
@@ -59,7 +60,7 @@
 	t.Run("closed range", func(t *testing.T) {
 		set, err := parseBuildReleaseSet("S-F1")
 		android.AssertDeepEquals(t, "errors", nil, err)
-		android.AssertStringEquals(t, "set", "[S,T,F1]", set.String())
+		android.AssertStringEquals(t, "set", "[S,Tiramisu,F1]", set.String())
 	})
 	invalidAReleaseMessage := `unknown release "A", expected one of ` + allBuildReleaseSet.String()
 	t.Run("invalid release", func(t *testing.T) {
@@ -78,7 +79,7 @@
 		android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage)
 	})
 	t.Run("invalid release in closed range end", func(t *testing.T) {
-		set, err := parseBuildReleaseSet("T-A")
+		set, err := parseBuildReleaseSet("Tiramisu-A")
 		android.AssertDeepEquals(t, "set", (*buildReleaseSet)(nil), set)
 		android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage)
 	})
@@ -125,61 +126,102 @@
 		F1_only string `supported_build_releases:"F1"`
 	}
 
-	type testBuildReleasePruner struct {
-		Default      string
-		S_and_T_only string `supported_build_releases:"S-T"`
-		T_later      string `supported_build_releases:"T+"`
-		Nested       nested
+	type mapped struct {
+		Default string
+		T_only  string `supported_build_releases:"Tiramisu"`
 	}
 
-	input := testBuildReleasePruner{
-		Default:      "Default",
-		S_and_T_only: "S_and_T_only",
-		T_later:      "T_later",
-		Nested: nested{
-			F1_only: "F1_only",
-		},
+	type testBuildReleasePruner struct {
+		Default      string
+		S_and_T_only string `supported_build_releases:"S-Tiramisu"`
+		T_later      string `supported_build_releases:"Tiramisu+"`
+		Nested       nested
+		Mapped       map[string]*mapped
+	}
+
+	inputFactory := func() testBuildReleasePruner {
+		return testBuildReleasePruner{
+			Default:      "Default",
+			S_and_T_only: "S_and_T_only",
+			T_later:      "T_later",
+			Nested: nested{
+				F1_only: "F1_only",
+			},
+			Mapped: map[string]*mapped{
+				"one": {
+					Default: "one-default",
+					T_only:  "one-t-only",
+				},
+				"two": {
+					Default: "two-default",
+					T_only:  "two-t-only",
+				},
+			},
+		}
+	}
+
+	marshal := func(t interface{}) string {
+		bytes, err := json.MarshalIndent(t, "", "  ")
+		if err != nil {
+			panic(err)
+		}
+		return string(bytes)
+	}
+
+	assertJsonEquals := func(t *testing.T, expected, actual interface{}) {
+		t.Helper()
+		expectedJson := marshal(expected)
+		actualJson := marshal(actual)
+		if actualJson != expectedJson {
+			t.Errorf("test struct: expected:\n%s\n got:\n%s", expectedJson, actualJson)
+		}
 	}
 
 	t.Run("target S", func(t *testing.T) {
-		testStruct := input
+		testStruct := inputFactory()
 		pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseS)
 		pruner.pruneProperties(&testStruct)
 
-		expected := input
+		expected := inputFactory()
 		expected.T_later = ""
 		expected.Nested.F1_only = ""
-		android.AssertDeepEquals(t, "test struct", expected, testStruct)
+		expected.Mapped["one"].T_only = ""
+		expected.Mapped["two"].T_only = ""
+		assertJsonEquals(t, expected, testStruct)
 	})
 
 	t.Run("target T", func(t *testing.T) {
-		testStruct := input
+		testStruct := inputFactory()
 		pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseT)
 		pruner.pruneProperties(&testStruct)
 
-		expected := input
+		expected := inputFactory()
 		expected.Nested.F1_only = ""
-		android.AssertDeepEquals(t, "test struct", expected, testStruct)
+		assertJsonEquals(t, expected, testStruct)
 	})
 
 	t.Run("target F1", func(t *testing.T) {
-		testStruct := input
+		testStruct := inputFactory()
 		pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseFuture1)
 		pruner.pruneProperties(&testStruct)
 
-		expected := input
+		expected := inputFactory()
 		expected.S_and_T_only = ""
-		android.AssertDeepEquals(t, "test struct", expected, testStruct)
+		expected.Mapped["one"].T_only = ""
+		expected.Mapped["two"].T_only = ""
+		assertJsonEquals(t, expected, testStruct)
 	})
 
 	t.Run("target F2", func(t *testing.T) {
-		testStruct := input
+		testStruct := inputFactory()
 		pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseFuture2)
 		pruner.pruneProperties(&testStruct)
 
-		expected := input
+		expected := inputFactory()
 		expected.S_and_T_only = ""
 		expected.Nested.F1_only = ""
-		android.AssertDeepEquals(t, "test struct", expected, testStruct)
+		expected.Mapped["one"].T_only = ""
+		expected.Mapped["two"].T_only = ""
+		assertJsonEquals(t, expected, testStruct)
 	})
 }
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 0d9b4a0..f0d3b35 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -1319,6 +1319,58 @@
 	)
 }
 
+func TestSnapshotWithJavaSdkLibrary_AnnotationsZip_PreT(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJavaSdkLibrary,
+		android.FixtureMergeEnv(map[string]string{
+			"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S",
+		}),
+	).RunTestWithBp(t, `
+		sdk {
+			name: "mysdk",
+			java_sdk_libs: ["myjavalib"],
+		}
+
+		java_sdk_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			sdk_version: "current",
+			shared_library: false,
+			annotations_enabled: true,
+			public: {
+				enabled: true,
+			},
+		}
+	`)
+
+	CheckSnapshot(t, result, "mysdk", "",
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "myjavalib",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    shared_library: false,
+    public: {
+        jars: ["sdk_library/public/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+        current_api: "sdk_library/public/myjavalib.txt",
+        removed_api: "sdk_library/public/myjavalib-removed.txt",
+        sdk_version: "current",
+    },
+}
+		`),
+		checkAllCopyRules(`
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt
+		`),
+		checkMergeZips(".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip"),
+	)
+}
+
 func TestSnapshotWithJavaSdkLibrary_CompileDex(t *testing.T) {
 	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index c32cde0..4190e84 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -42,8 +42,6 @@
 	pctx.Import("android/soong/android")
 
 	registerShBuildComponents(android.InitRegistrationContext)
-
-	android.RegisterBp2BuildMutator("sh_binary", ShBinaryBp2Build)
 }
 
 func registerShBuildComponents(ctx android.RegistrationContext) {
@@ -168,10 +166,6 @@
 
 var _ android.HostToolProvider = (*ShBinary)(nil)
 
-func (s *ShBinary) InstallBypassMake() bool {
-	return true
-}
-
 type ShTest struct {
 	ShBinary
 
@@ -274,6 +268,9 @@
 func (s *ShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	s.generateAndroidBuildActions(ctx)
 	installDir := android.PathForModuleInstall(ctx, "bin", proptools.String(s.properties.Sub_dir))
+	if !s.Installable() {
+		s.SkipInstall()
+	}
 	s.installedFile = ctx.InstallExecutable(installDir, s.outputFilePath.Base(), s.outputFilePath)
 	for _, symlink := range s.Symlinks() {
 		ctx.InstallSymlink(installDir, symlink, s.installedFile)
@@ -289,6 +286,7 @@
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				s.customAndroidMkEntries(entries)
 				entries.SetString("LOCAL_MODULE_RELATIVE_PATH", proptools.String(s.properties.Sub_dir))
+				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !s.Installable())
 			},
 		},
 	}}
@@ -437,7 +435,7 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				s.customAndroidMkEntries(entries)
-				entries.SetPath("LOCAL_MODULE_PATH", s.installDir.ToMakePath())
+				entries.SetPath("LOCAL_MODULE_PATH", s.installDir)
 				entries.AddCompatibilityTestSuites(s.testProperties.Test_suites...)
 				if s.testConfig != nil {
 					entries.SetPath("LOCAL_FULL_TEST_CONFIG", s.testConfig)
@@ -460,6 +458,9 @@
 					dir := strings.TrimSuffix(s.dataModules[relPath].String(), relPath)
 					entries.AddStrings("LOCAL_TEST_DATA", dir+":"+relPath)
 				}
+				if s.testProperties.Data_bins != nil {
+					entries.AddStrings("LOCAL_TEST_DATA_BINS", s.testProperties.Data_bins...)
+				}
 				if Bool(s.testProperties.Test_options.Unit_test) {
 					entries.SetBool("LOCAL_IS_UNIT_TEST", true)
 				}
@@ -516,7 +517,9 @@
 }
 
 type bazelShBinaryAttributes struct {
-	Srcs bazel.LabelListAttribute
+	Srcs     bazel.LabelListAttribute
+	Filename *string
+	Sub_dir  *string
 	// Bazel also supports the attributes below, but (so far) these are not required for Bionic
 	// deps
 	// data
@@ -538,21 +541,29 @@
 	// visibility
 }
 
-func ShBinaryBp2Build(ctx android.TopDownMutatorContext) {
-	m, ok := ctx.Module().(*ShBinary)
-	if !ok || !m.ConvertWithBp2build(ctx) {
-		return
-	}
-
+func (m *ShBinary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
 	srcs := bazel.MakeLabelListAttribute(
 		android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src}))
 
+	var filename *string
+	if m.properties.Filename != nil {
+		filename = m.properties.Filename
+	}
+
+	var subDir *string
+	if m.properties.Sub_dir != nil {
+		subDir = m.properties.Sub_dir
+	}
+
 	attrs := &bazelShBinaryAttributes{
-		Srcs: srcs,
+		Srcs:     srcs,
+		Filename: filename,
+		Sub_dir:  subDir,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
-		Rule_class: "sh_binary",
+		Rule_class:        "sh_binary",
+		Bzl_load_location: "//build/bazel/rules:sh_binary.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 865d5f3..89b8126 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -4,6 +4,7 @@
 	"os"
 	"path/filepath"
 	"strconv"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -36,13 +37,18 @@
 //
 // deprecated
 func testShBinary(t *testing.T, bp string) (*android.TestContext, android.Config) {
+	bp = bp + cc.GatherRequiredDepsForTest(android.Android)
+
 	result := prepareForShTest.RunTestWithBp(t, bp)
 
 	return result.TestContext, result.Config
 }
 
 func TestShTestSubDir(t *testing.T) {
-	ctx, config := testShBinary(t, `
+	result := android.GroupFixturePreparers(
+		prepareForShTest,
+		android.FixtureModifyConfig(android.SetKatiEnabledForTests),
+	).RunTestWithBp(t, `
 		sh_test {
 			name: "foo",
 			src: "test.sh",
@@ -50,17 +56,20 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
+	mod := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
 
-	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
+	entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
 
 	expectedPath := "out/target/product/test_device/data/nativetest64/foo_test"
 	actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0]
-	android.AssertStringPathRelativeToTopEquals(t, "LOCAL_MODULE_PATH[0]", config, expectedPath, actualPath)
+	android.AssertStringPathRelativeToTopEquals(t, "LOCAL_MODULE_PATH[0]", result.Config, expectedPath, actualPath)
 }
 
 func TestShTest(t *testing.T) {
-	ctx, config := testShBinary(t, `
+	result := android.GroupFixturePreparers(
+		prepareForShTest,
+		android.FixtureModifyConfig(android.SetKatiEnabledForTests),
+	).RunTestWithBp(t, `
 		sh_test {
 			name: "foo",
 			src: "test.sh",
@@ -72,13 +81,13 @@
 		}
 	`)
 
-	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
+	mod := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
 
-	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
+	entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0]
 
 	expectedPath := "out/target/product/test_device/data/nativetest64/foo"
 	actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0]
-	android.AssertStringPathRelativeToTopEquals(t, "LOCAL_MODULE_PATH[0]", config, expectedPath, actualPath)
+	android.AssertStringPathRelativeToTopEquals(t, "LOCAL_MODULE_PATH[0]", result.Config, expectedPath, actualPath)
 
 	expectedData := []string{":testdata/data1", ":testdata/sub/data2"}
 	actualData := entries.EntryMap["LOCAL_TEST_DATA"]
@@ -209,3 +218,40 @@
 	actualData := entries.EntryMap["LOCAL_TEST_DATA"]
 	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_TEST_DATA", config, expectedData, actualData)
 }
+
+func TestShTestHost_dataDeviceModulesAutogenTradefedConfig(t *testing.T) {
+	ctx, config := testShBinary(t, `
+		sh_test_host {
+			name: "foo",
+			src: "test.sh",
+			data_device_bins: ["bar"],
+			data_device_libs: ["libbar"],
+		}
+
+		cc_binary {
+			name: "bar",
+			shared_libs: ["libbar"],
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libbar",
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	buildOS := config.BuildOS.String()
+	fooModule := ctx.ModuleForTests("foo", buildOS+"_x86_64")
+
+	expectedBinAutogenConfig := `<option name="push-file" key="bar" value="/data/local/tests/unrestricted/foo/bar" />`
+	autogen := fooModule.Rule("autogen")
+	if !strings.Contains(autogen.Args["extraConfigs"], expectedBinAutogenConfig) {
+		t.Errorf("foo extraConfings %v does not contain %q", autogen.Args["extraConfigs"], expectedBinAutogenConfig)
+	}
+}
diff --git a/shared/Android.bp b/shared/Android.bp
index deb17f8..3c84f55 100644
--- a/shared/Android.bp
+++ b/shared/Android.bp
@@ -9,11 +9,13 @@
         "env.go",
         "paths.go",
         "debug.go",
+        "proto.go",
     ],
     testSrcs: [
         "paths_test.go",
     ],
     deps: [
         "soong-bazel",
+        "golang-protobuf-proto",
     ],
 }
diff --git a/shared/proto.go b/shared/proto.go
new file mode 100644
index 0000000..232656b
--- /dev/null
+++ b/shared/proto.go
@@ -0,0 +1,41 @@
+// 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 shared
+
+import (
+	"io/ioutil"
+	"os"
+
+	"google.golang.org/protobuf/proto"
+)
+
+// Save takes a protobuf message, marshals to an array of bytes
+// and is then saved to a file.
+func Save(pb proto.Message, filepath string) (err error) {
+	data, err := proto.Marshal(pb)
+	if err != nil {
+		return err
+	}
+	tempFilepath := filepath + ".tmp"
+	if err := ioutil.WriteFile(tempFilepath, []byte(data), 0644 /* rw-r--r-- */); err != nil {
+		return err
+	}
+
+	if err := os.Rename(tempFilepath, filepath); err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go
index 2a25a00..09a382e 100644
--- a/snapshot/host_snapshot.go
+++ b/snapshot/host_snapshot.go
@@ -58,7 +58,7 @@
 	android.ModuleBase
 	android.PackagingBase
 
-	zipFile    android.OptionalPath
+	outputFile android.OutputPath
 	installDir android.InstallPath
 }
 
@@ -141,7 +141,7 @@
 	// 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.outputFile = android.PathForModuleOut(ctx, f.installFileName()).OutputPath
 
 	f.installDir = android.PathForModuleInstall(ctx)
 
@@ -158,29 +158,24 @@
 
 	builder.Command().
 		BuiltTool("merge_zips").
-		Output(outputFile).
+		Output(f.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)
+	ctx.InstallFile(f.installDir, f.installFileName(), f.outputFile)
 
 }
 
 // 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()),
+		OutputFile: android.OptionalPathForPath(f.outputFile),
+		DistFiles:  android.MakeDefaultDistFiles(f.outputFile),
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-				entries.SetString("LOCAL_MODULE_PATH", f.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_PATH", f.installDir.String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName())
 			},
 		},
diff --git a/starlark_fmt/Android.bp b/starlark_fmt/Android.bp
new file mode 100644
index 0000000..8d80ccd
--- /dev/null
+++ b/starlark_fmt/Android.bp
@@ -0,0 +1,28 @@
+// Copyright 2022 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-starlark-format",
+    pkgPath: "android/soong/starlark_fmt",
+    srcs: [
+        "format.go",
+    ],
+    testSrcs: [
+        "format_test.go",
+    ],
+}
diff --git a/starlark_fmt/format.go b/starlark_fmt/format.go
new file mode 100644
index 0000000..23eee59
--- /dev/null
+++ b/starlark_fmt/format.go
@@ -0,0 +1,96 @@
+// Copyright 2022 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 starlark_fmt
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+)
+
+const (
+	indent = 4
+)
+
+// Indention returns an indent string of the specified level.
+func Indention(level int) string {
+	if level < 0 {
+		panic(fmt.Errorf("indent level cannot be less than 0, but got %d", level))
+	}
+	return strings.Repeat(" ", level*indent)
+}
+
+// PrintBool returns a Starlark compatible bool string.
+func PrintBool(item bool) string {
+	return strings.Title(fmt.Sprintf("%t", item))
+}
+
+// PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels.
+func PrintStringList(items []string, indentLevel int) string {
+	return PrintList(items, indentLevel, `"%s"`)
+}
+
+// PrintList returns a Starlark-compatible string of list formmated as requested.
+func PrintList(items []string, indentLevel int, formatString string) string {
+	if len(items) == 0 {
+		return "[]"
+	} else if len(items) == 1 {
+		return fmt.Sprintf("["+formatString+"]", items[0])
+	}
+	list := make([]string, 0, len(items)+2)
+	list = append(list, "[")
+	innerIndent := Indention(indentLevel + 1)
+	for _, item := range items {
+		list = append(list, fmt.Sprintf(`%s`+formatString+`,`, innerIndent, item))
+	}
+	list = append(list, Indention(indentLevel)+"]")
+	return strings.Join(list, "\n")
+}
+
+// PrintStringListDict returns a Starlark-compatible string formatted as dictionary with
+// string keys and list of string values.
+func PrintStringListDict(dict map[string][]string, indentLevel int) string {
+	formattedValueDict := make(map[string]string, len(dict))
+	for k, v := range dict {
+		formattedValueDict[k] = PrintStringList(v, indentLevel+1)
+	}
+	return PrintDict(formattedValueDict, indentLevel)
+}
+
+// PrintBoolDict returns a starlark-compatible string containing a dictionary with string keys and
+// values printed with no additional formatting.
+func PrintBoolDict(dict map[string]bool, indentLevel int) string {
+	formattedValueDict := make(map[string]string, len(dict))
+	for k, v := range dict {
+		formattedValueDict[k] = PrintBool(v)
+	}
+	return PrintDict(formattedValueDict, indentLevel)
+}
+
+// PrintDict returns a starlark-compatible string containing a dictionary with string keys and
+// values printed with no additional formatting.
+func PrintDict(dict map[string]string, indentLevel int) string {
+	if len(dict) == 0 {
+		return "{}"
+	}
+	items := make([]string, 0, len(dict))
+	for k, v := range dict {
+		items = append(items, fmt.Sprintf(`%s"%s": %s,`, Indention(indentLevel+1), k, v))
+	}
+	sort.Strings(items)
+	return fmt.Sprintf(`{
+%s
+%s}`, strings.Join(items, "\n"), Indention(indentLevel))
+}
diff --git a/starlark_fmt/format_test.go b/starlark_fmt/format_test.go
new file mode 100644
index 0000000..90f78ef
--- /dev/null
+++ b/starlark_fmt/format_test.go
@@ -0,0 +1,169 @@
+// Copyright 2022 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 starlark_fmt
+
+import (
+	"testing"
+)
+
+func TestPrintEmptyStringList(t *testing.T) {
+	in := []string{}
+	indentLevel := 0
+	out := PrintStringList(in, indentLevel)
+	expectedOut := "[]"
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintSingleElementStringList(t *testing.T) {
+	in := []string{"a"}
+	indentLevel := 0
+	out := PrintStringList(in, indentLevel)
+	expectedOut := `["a"]`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintMultiElementStringList(t *testing.T) {
+	in := []string{"a", "b"}
+	indentLevel := 0
+	out := PrintStringList(in, indentLevel)
+	expectedOut := `[
+    "a",
+    "b",
+]`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintEmptyList(t *testing.T) {
+	in := []string{}
+	indentLevel := 0
+	out := PrintList(in, indentLevel, "%s")
+	expectedOut := "[]"
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintSingleElementList(t *testing.T) {
+	in := []string{"1"}
+	indentLevel := 0
+	out := PrintList(in, indentLevel, "%s")
+	expectedOut := `[1]`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintMultiElementList(t *testing.T) {
+	in := []string{"1", "2"}
+	indentLevel := 0
+	out := PrintList(in, indentLevel, "%s")
+	expectedOut := `[
+    1,
+    2,
+]`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestListWithNonZeroIndent(t *testing.T) {
+	in := []string{"1", "2"}
+	indentLevel := 1
+	out := PrintList(in, indentLevel, "%s")
+	expectedOut := `[
+        1,
+        2,
+    ]`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestStringListDictEmpty(t *testing.T) {
+	in := map[string][]string{}
+	indentLevel := 0
+	out := PrintStringListDict(in, indentLevel)
+	expectedOut := `{}`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestStringListDict(t *testing.T) {
+	in := map[string][]string{
+		"key1": []string{},
+		"key2": []string{"a"},
+		"key3": []string{"1", "2"},
+	}
+	indentLevel := 0
+	out := PrintStringListDict(in, indentLevel)
+	expectedOut := `{
+    "key1": [],
+    "key2": ["a"],
+    "key3": [
+        "1",
+        "2",
+    ],
+}`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintDict(t *testing.T) {
+	in := map[string]string{
+		"key1": `""`,
+		"key2": `"a"`,
+		"key3": `[
+        1,
+        2,
+    ]`,
+	}
+	indentLevel := 0
+	out := PrintDict(in, indentLevel)
+	expectedOut := `{
+    "key1": "",
+    "key2": "a",
+    "key3": [
+        1,
+        2,
+    ],
+}`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
+
+func TestPrintDictWithIndent(t *testing.T) {
+	in := map[string]string{
+		"key1": `""`,
+		"key2": `"a"`,
+	}
+	indentLevel := 1
+	out := PrintDict(in, indentLevel)
+	expectedOut := `{
+        "key1": "",
+        "key2": "a",
+    }`
+	if out != expectedOut {
+		t.Errorf("Expected %q, got %q", expectedOut, out)
+	}
+}
diff --git a/tests/androidmk_test.sh b/tests/androidmk_test.sh
new file mode 100755
index 0000000..331dc77
--- /dev/null
+++ b/tests/androidmk_test.sh
@@ -0,0 +1,135 @@
+#!/bin/bash -eu
+
+set -o pipefail
+
+# How to run: bash path-to-script/androidmk_test.sh
+# Tests of converting license functionality of the androidmk tool
+REAL_TOP="$(readlink -f "$(dirname "$0")"/../../..)"
+$REAL_TOP/build/soong/soong_ui.bash --make-mode androidmk
+
+source "$(dirname "$0")/lib.sh"
+
+# Expect to create a new license module
+function test_rewrite_license_property_inside_current_directory {
+  setup
+
+  # Create an Android.mk file
+  mkdir -p a/b
+  cat > a/b/Android.mk <<'EOF'
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_LICENSE_KINDS := license_kind1 license_kind2
+LOCAL_LICENSE_CONDITIONS := license_condition
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/license_notice1 $(LOCAL_PATH)/license_notice2
+include $(BUILD_PACKAGE)
+EOF
+
+  # Create an expected Android.bp file for the module "foo"
+  cat > a/b/Android.bp <<'EOF'
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "a_b_license",
+    ],
+}
+
+license {
+    name: "a_b_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "license_kind1",
+        "license_kind2",
+    ],
+    license_text: [
+        "license_notice1",
+        "license_notice2",
+    ],
+}
+
+android_app {
+    name: "foo",
+}
+EOF
+
+  run_androidmk_test "a/b/Android.mk" "a/b/Android.bp"
+}
+
+# Expect to reference to an existing license module
+function test_rewrite_license_property_outside_current_directory {
+  setup
+
+  # Create an Android.mk file
+  mkdir -p a/b/c/d
+  cat > a/b/c/d/Android.mk <<'EOF'
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_LICENSE_KINDS := license_kind1 license_kind2
+LOCAL_LICENSE_CONDITIONS := license_condition
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../license_notice1 $(LOCAL_PATH)/../../license_notice2
+include $(BUILD_PACKAGE)
+EOF
+
+  # Create an expected (input) Android.bp file at a/b/
+  cat > a/b/Android.bp <<'EOF'
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "a_b_license",
+    ],
+}
+
+license {
+    name: "a_b_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "license_kind1",
+        "license_kind2",
+    ],
+    license_text: [
+        "license_notice1",
+        "license_notice2",
+    ],
+}
+
+android_app {
+    name: "bar",
+}
+EOF
+
+  # Create an expected (output) Android.bp file for the module "foo"
+  cat > a/b/c/d/Android.bp <<'EOF'
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "a_b_license",
+    ],
+}
+
+android_app {
+    name: "foo",
+}
+EOF
+
+  run_androidmk_test "a/b/c/d/Android.mk" "a/b/c/d/Android.bp"
+}
+
+run_androidmk_test () {
+  export ANDROID_BUILD_TOP="$MOCK_TOP"
+
+  local out=$($REAL_TOP/*/host/*/bin/androidmk "$1")
+  local expected=$(<"$2")
+
+  if [[ "$out" != "$expected" ]]; then
+    ANDROID_BUILD_TOP="$REAL_TOP"
+    cleanup_mock_top
+    fail "The output is not the same as the expected"
+  fi
+
+  ANDROID_BUILD_TOP="$REAL_TOP"
+  cleanup_mock_top
+  echo "Succeeded"
+}
+
+test_rewrite_license_property_inside_current_directory
+
+test_rewrite_license_property_outside_current_directory
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index b19949a..76a918b 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -3,6 +3,7 @@
 set -o pipefail
 
 TOP="$(readlink -f "$(dirname "$0")"/../../..)"
+"$TOP/build/soong/tests/androidmk_test.sh"
 "$TOP/build/soong/tests/bootstrap_test.sh"
 "$TOP/build/soong/tests/mixed_mode_test.sh"
 "$TOP/build/soong/tests/bp2build_bazel_test.sh"
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index da55829..c2429ab 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -188,20 +188,20 @@
 }
 
 func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string,
-	testSuites []string, autoGenConfig *bool, unitTest *bool) android.Path {
+	testSuites []string, config []Config, autoGenConfig *bool, unitTest *bool) android.Path {
 	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
-			autogenTemplate(ctx, autogenPath, templatePath.String(), nil, "")
+			autogenTemplate(ctx, autogenPath, templatePath.String(), config, "")
 		} else {
 			if ctx.Device() {
-				autogenTemplate(ctx, autogenPath, "${JavaTestConfigTemplate}", nil, "")
+				autogenTemplate(ctx, autogenPath, "${JavaTestConfigTemplate}", config, "")
 			} else {
 				if Bool(unitTest) {
-					autogenTemplate(ctx, autogenPath, "${JavaHostUnitTestConfigTemplate}", nil, "")
+					autogenTemplate(ctx, autogenPath, "${JavaHostUnitTestConfigTemplate}", config, "")
 				} else {
-					autogenTemplate(ctx, autogenPath, "${JavaHostTestConfigTemplate}", nil, "")
+					autogenTemplate(ctx, autogenPath, "${JavaHostTestConfigTemplate}", config, "")
 				}
 			}
 		}
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index a3a1aaf..1c80cff 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -171,6 +171,7 @@
 		productOut("recovery"),
 		productOut("root"),
 		productOut("system"),
+		productOut("system_dlkm"),
 		productOut("system_other"),
 		productOut("vendor"),
 		productOut("vendor_dlkm"),
diff --git a/ui/build/config.go b/ui/build/config.go
index c306633..1dd948c 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -15,8 +15,12 @@
 package build
 
 import (
+	"context"
+	"encoding/json"
 	"fmt"
+	"io/ioutil"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"runtime"
 	"strconv"
@@ -30,6 +34,14 @@
 	smpb "android/soong/ui/metrics/metrics_proto"
 )
 
+const (
+	envConfigDir  = "vendor/google/tools/soong_config"
+	jsonSuffix    = "json"
+
+	configFetcher = "vendor/google/tools/soong/expconfigfetcher"
+	envConfigFetchTimeout = 10 * time.Second
+)
+
 type Config struct{ *configImpl }
 
 type configImpl struct {
@@ -128,6 +140,85 @@
 	}
 }
 
+// fetchEnvConfig optionally fetches environment config from an
+// experiments system to control Soong features dynamically.
+func fetchEnvConfig(ctx Context, config *configImpl, envConfigName string) error {
+	s, err := os.Stat(configFetcher)
+	if err != nil {
+		if os.IsNotExist(err) {
+			return nil
+		}
+		return err
+	}
+	if s.Mode()&0111 == 0 {
+		return fmt.Errorf("configuration fetcher binary %v is not executable: %v", configFetcher, s.Mode())
+	}
+
+	configExists := false
+	outConfigFilePath := filepath.Join(config.OutDir(), envConfigName + jsonSuffix)
+	if _, err := os.Stat(outConfigFilePath); err == nil {
+		configExists = true
+	}
+
+	tCtx, cancel := context.WithTimeout(ctx, envConfigFetchTimeout)
+	defer cancel()
+	cmd := exec.CommandContext(tCtx, configFetcher, "-output_config_dir", config.OutDir())
+	if err := cmd.Start(); err != nil {
+		return err
+	}
+
+	// If a config file already exists, return immediately and run the config file
+	// fetch in the background. Otherwise, wait for the config file to be fetched.
+	if configExists {
+		go cmd.Wait()
+		return nil
+	}
+	if err := cmd.Wait(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func loadEnvConfig(ctx Context, config *configImpl) error {
+	bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
+	if bc == "" {
+		return nil
+	}
+
+	if err := fetchEnvConfig(ctx, config, bc); err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to fetch config file: %v", err)
+	}
+
+	configDirs := []string{
+		config.OutDir(),
+		os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR"),
+		envConfigDir,
+	}
+	for _, dir := range configDirs {
+		cfgFile := filepath.Join(os.Getenv("TOP"), dir, fmt.Sprintf("%s.%s", bc, jsonSuffix))
+		envVarsJSON, err := ioutil.ReadFile(cfgFile)
+		if err != nil {
+			continue
+		}
+		ctx.Verbosef("Loading config file %v\n", cfgFile)
+		var envVars map[string]map[string]string
+		if err := json.Unmarshal(envVarsJSON, &envVars); err != nil {
+			fmt.Fprintf(os.Stderr, "Env vars config file %s did not parse correctly: %s", cfgFile, err.Error())
+			continue
+		}
+		for k, v := range envVars["env"] {
+			if os.Getenv(k) != "" {
+				continue
+			}
+			config.environ.Set(k, v)
+		}
+		ctx.Verbosef("Finished loading config file %v\n", cfgFile)
+		break
+	}
+
+	return nil
+}
+
 func NewConfig(ctx Context, args ...string) Config {
 	ret := &configImpl{
 		environ:       OsEnvironment(),
@@ -157,6 +248,12 @@
 		ret.environ.Set("OUT_DIR", outDir)
 	}
 
+	// loadEnvConfig needs to know what the OUT_DIR is, so it should
+	// be called after we determine the appropriate out directory.
+	if err := loadEnvConfig(ctx, ret); err != nil {
+		ctx.Fatalln("Failed to parse env config files: %v", err)
+	}
+
 	if distDir, ok := ret.environ.Get("DIST_DIR"); ok {
 		ret.distDir = filepath.Clean(distDir)
 	} else {
@@ -721,10 +818,6 @@
 }
 
 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
@@ -736,6 +829,11 @@
 		return true
 	}
 
+	// bp2build + dist may be used to dist bp2build logs but does not require SoongBuildInvocation
+	if c.Dist() && !c.Bp2Build() {
+		return true
+	}
+
 	// build.ninja doesn't need to be generated
 	return false
 }
@@ -817,6 +915,10 @@
 	return shared.JoinPath(c.SoongOutDir(), "module-graph.json")
 }
 
+func (c *configImpl) ModuleActionsFile() string {
+	return shared.JoinPath(c.SoongOutDir(), "module-actions.json")
+}
+
 func (c *configImpl) TempDir() string {
 	return shared.TempDirForOutDir(c.SoongOutDir())
 }
@@ -987,7 +1089,7 @@
 }
 
 func (c *configImpl) UseRBE() bool {
-	if v, ok := c.environ.Get("USE_RBE"); ok {
+	if v, ok := c.Environment().Get("USE_RBE"); ok {
 		v = strings.TrimSpace(v)
 		if v != "" && v != "false" {
 			return true
@@ -1255,16 +1357,22 @@
 	return c.metricsUploader
 }
 
-// LogsDir returns the logs directory where build log and metrics
-// files are located. By default, the logs directory is the out
+// LogsDir returns the absolute path to the logs directory where build log and
+// metrics files are located. By default, the logs directory is the out
 // directory. If the argument dist is specified, the logs directory
 // is <dist_dir>/logs.
 func (c *configImpl) LogsDir() string {
+	dir := c.OutDir()
 	if c.Dist() {
 		// Always write logs to the real dist dir, even if Bazel is using a rigged dist dir for other files
-		return filepath.Join(c.RealDistDir(), "logs")
+		dir = filepath.Join(c.RealDistDir(), "logs")
 	}
-	return c.OutDir()
+	absDir, err := filepath.Abs(dir)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "\nError making log dir '%s' absolute: %s\n", dir, err.Error())
+		os.Exit(1)
+	}
+	return absDir
 }
 
 // BazelMetricsDir returns the <logs dir>/bazel_metrics directory
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 8f74969..68efe21 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -87,8 +87,8 @@
 			// Bazel top-level file to mark a directory as a Bazel workspace.
 			"WORKSPACE",
 		},
-		// Bazel Starlark configuration files.
-		IncludeSuffixes: []string{".bzl"},
+		// Bazel Starlark configuration files and all .mk files for product/board configuration.
+		IncludeSuffixes: []string{".bzl", ".mk"},
 	}
 	dumpDir := config.FileListDir()
 	f, err = finder.New(cacheParams, filesystem, logger.New(ioutil.Discard),
@@ -110,6 +110,19 @@
 	return entries.DirNames, matches
 }
 
+func findProductAndBoardConfigFiles(entries finder.DirEntries) (dirNames []string, fileNames []string) {
+	matches := []string{}
+	for _, foundName := range entries.FileNames {
+		if foundName != "Android.mk" &&
+			foundName != "AndroidProducts.mk" &&
+			foundName != "CleanSpec.mk" &&
+			strings.HasSuffix(foundName, ".mk") {
+			matches = append(matches, foundName)
+		}
+	}
+	return entries.DirNames, matches
+}
+
 // FindSources searches for source files known to <f> and writes them to the filesystem for
 // use later.
 func FindSources(ctx Context, config Config, f *finder.Finder) {
@@ -172,6 +185,13 @@
 		ctx.Fatalf("Could not find modules: %v", err)
 	}
 
+	// Recursively look for all product/board config files.
+	configurationFiles := f.FindMatching(".", findProductAndBoardConfigFiles)
+	err = dumpListToFile(ctx, config, configurationFiles, filepath.Join(dumpDir, "configuration.list"))
+	if err != nil {
+		ctx.Fatalf("Could not export product/board configuration list: %v", err)
+	}
+
 	if config.Dist() {
 		f.WaitForDbDump()
 		// Dist the files.db plain text database.
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 5961c45..41de6bd 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -141,6 +141,8 @@
 
 			// RBE client
 			"RBE_compare",
+			"RBE_num_local_reruns",
+			"RBE_num_remote_reruns",
 			"RBE_exec_root",
 			"RBE_exec_strategy",
 			"RBE_invocation_id",
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index d74f262..8f9a699 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -19,6 +19,7 @@
 	"math/rand"
 	"os"
 	"path/filepath"
+	"runtime"
 	"syscall"
 	"time"
 
@@ -87,6 +88,13 @@
 		}
 		vars["RBE_server_address"] = fmt.Sprintf("unix://%v", name)
 	}
+
+	rf := 1.0
+	if config.Parallel() < runtime.NumCPU() {
+		rf = float64(config.Parallel()) / float64(runtime.NumCPU())
+	}
+	vars["RBE_local_resource_fraction"] = fmt.Sprintf("%.2f", rf)
+
 	k, v := config.rbeAuth()
 	vars[k] = v
 	return vars
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 4ced722..117a2a5 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -153,7 +153,12 @@
 	return true, nil
 }
 
-func primaryBuilderInvocation(config Config, name string, output string, specificArgs []string) bootstrap.PrimaryBuilderInvocation {
+func primaryBuilderInvocation(
+	config Config,
+	name string,
+	output string,
+	specificArgs []string,
+	description string) bootstrap.PrimaryBuilderInvocation {
 	commonArgs := make([]string, 0, 0)
 
 	if !config.skipSoongTests {
@@ -161,10 +166,22 @@
 	}
 
 	commonArgs = append(commonArgs, "-l", filepath.Join(config.FileListDir(), "Android.bp.list"))
+	invocationEnv := make(map[string]string)
+	debugMode := os.Getenv("SOONG_DELVE") != ""
 
-	if os.Getenv("SOONG_DELVE") != "" {
+	if debugMode {
 		commonArgs = append(commonArgs, "--delve_listen", os.Getenv("SOONG_DELVE"))
 		commonArgs = append(commonArgs, "--delve_path", shared.ResolveDelveBinary())
+		// GODEBUG=asyncpreemptoff=1 disables the preemption of goroutines. This
+		// is useful because the preemption happens by sending SIGURG to the OS
+		// thread hosting the goroutine in question and each signal results in
+		// work that needs to be done by Delve; it uses ptrace to debug the Go
+		// process and the tracer process must deal with every signal (it is not
+		// possible to selectively ignore SIGURG). This makes debugging slower,
+		// sometimes by an order of magnitude depending on luck.
+		// The original reason for adding async preemption to Go is here:
+		// https://github.com/golang/proposal/blob/master/design/24543-non-cooperative-preemption.md
+		invocationEnv["GODEBUG"] = "asyncpreemptoff=1"
 	}
 
 	allArgs := make([]string, 0, 0)
@@ -178,9 +195,16 @@
 	allArgs = append(allArgs, "Android.bp")
 
 	return bootstrap.PrimaryBuilderInvocation{
-		Inputs:  []string{"Android.bp"},
-		Outputs: []string{output},
-		Args:    allArgs,
+		Inputs:      []string{"Android.bp"},
+		Outputs:     []string{output},
+		Args:        allArgs,
+		Description: description,
+		// NB: Changing the value of this environment variable will not result in a
+		// rebuild. The bootstrap Ninja file will change, but apparently Ninja does
+		// not consider changing the pool specified in a statement a change that's
+		// worth rebuilding for.
+		Console: os.Getenv("SOONG_UNBUFFERED_OUTPUT") == "1",
+		Env:     invocationEnv,
 	}
 }
 
@@ -232,7 +256,9 @@
 		config,
 		soongBuildTag,
 		config.SoongNinjaFile(),
-		mainSoongBuildExtraArgs)
+		mainSoongBuildExtraArgs,
+		fmt.Sprintf("analyzing Android.bp files and generating ninja file at %s", config.SoongNinjaFile()),
+	)
 
 	if config.bazelBuildMode() == mixedBuild {
 		// Mixed builds call Bazel from soong_build and they therefore need the
@@ -248,7 +274,9 @@
 		config.Bp2BuildMarkerFile(),
 		[]string{
 			"--bp2build_marker", config.Bp2BuildMarkerFile(),
-		})
+		},
+		fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()),
+	)
 
 	jsonModuleGraphInvocation := primaryBuilderInvocation(
 		config,
@@ -256,15 +284,21 @@
 		config.ModuleGraphFile(),
 		[]string{
 			"--module_graph_file", config.ModuleGraphFile(),
-		})
+			"--module_actions_file", config.ModuleActionsFile(),
+		},
+		fmt.Sprintf("generating the Soong module graph at %s", config.ModuleGraphFile()),
+	)
 
+	queryviewDir := filepath.Join(config.SoongOutDir(), "queryview")
 	queryviewInvocation := primaryBuilderInvocation(
 		config,
 		queryviewTag,
 		config.QueryviewMarkerFile(),
 		[]string{
-			"--bazel_queryview_dir", filepath.Join(config.SoongOutDir(), "queryview"),
-		})
+			"--bazel_queryview_dir", queryviewDir,
+		},
+		fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
+	)
 
 	soongDocsInvocation := primaryBuilderInvocation(
 		config,
@@ -272,7 +306,9 @@
 		config.SoongDocsHtml(),
 		[]string{
 			"--soong_docs", config.SoongDocsHtml(),
-		})
+		},
+		fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()),
+	)
 
 	globFiles := []string{
 		config.NamedGlobFile(soongBuildTag),
@@ -356,6 +392,7 @@
 	soongBuildEnv.Set("BAZEL_OUTPUT_BASE", filepath.Join(config.BazelOutDir(), "output"))
 	soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
 	soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
+	soongBuildEnv.Set("LOG_DIR", config.LogsDir())
 
 	// For Soong bootstrapping tests
 	if os.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 96f6389..3ba3907 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -25,6 +25,7 @@
         "soong-ui-metrics_proto",
         "soong-ui-bp2build_metrics_proto",
         "soong-ui-tracer",
+        "soong-shared",
     ],
     srcs: [
         "metrics.go",
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
index 11177e4..95f02ca 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
@@ -49,6 +49,10 @@
 	RuleClassCount map[string]uint64 `protobuf:"bytes,4,rep,name=ruleClassCount,proto3" json:"ruleClassCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
 	// List of converted modules
 	ConvertedModules []string `protobuf:"bytes,5,rep,name=convertedModules,proto3" json:"convertedModules,omitempty"`
+	// Counts of converted modules by module type.
+	ConvertedModuleTypeCount map[string]uint64 `protobuf:"bytes,6,rep,name=convertedModuleTypeCount,proto3" json:"convertedModuleTypeCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
+	// Counts of total modules by module type.
+	TotalModuleTypeCount map[string]uint64 `protobuf:"bytes,7,rep,name=totalModuleTypeCount,proto3" json:"totalModuleTypeCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
 }
 
 func (x *Bp2BuildMetrics) Reset() {
@@ -118,13 +122,27 @@
 	return nil
 }
 
+func (x *Bp2BuildMetrics) GetConvertedModuleTypeCount() map[string]uint64 {
+	if x != nil {
+		return x.ConvertedModuleTypeCount
+	}
+	return nil
+}
+
+func (x *Bp2BuildMetrics) GetTotalModuleTypeCount() map[string]uint64 {
+	if x != nil {
+		return x.TotalModuleTypeCount
+	}
+	return nil
+}
+
 var File_bp2build_metrics_proto protoreflect.FileDescriptor
 
 var file_bp2build_metrics_proto_rawDesc = []byte{
 	0x0a, 0x16, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
 	0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
 	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
-	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x8f, 0x03, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
+	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xac, 0x06, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
 	0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x67, 0x65,
 	0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75,
 	0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
@@ -145,15 +163,40 @@
 	0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x6f, 0x6e,
 	0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20,
 	0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f,
-	0x64, 0x75, 0x6c, 0x65, 0x73, 0x1a, 0x41, 0x0a, 0x13, 0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61,
-	0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
-	0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14,
-	0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76,
-	0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x31, 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72,
-	0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74,
-	0x72, 0x69, 0x63, 0x73, 0x2f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
-	0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x33,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x87, 0x01, 0x0a, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72,
+	0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75,
+	0x6e, 0x74, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32, 0x42, 0x75, 0x69, 0x6c, 0x64,
+	0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65,
+	0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+	0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64,
+	0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
+	0x7b, 0x0a, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79,
+	0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e,
+	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x70, 0x32,
+	0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x54, 0x6f, 0x74,
+	0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e,
+	0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64,
+	0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x41, 0x0a, 0x13,
+	0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e,
+	0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a,
+	0x4b, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75,
+	0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
+	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
+	0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x47, 0x0a, 0x19,
+	0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43,
+	0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
+	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
+	0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x31, 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x2f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -168,18 +211,22 @@
 	return file_bp2build_metrics_proto_rawDescData
 }
 
-var file_bp2build_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_bp2build_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
 var file_bp2build_metrics_proto_goTypes = []interface{}{
 	(*Bp2BuildMetrics)(nil), // 0: soong_build_bp2build_metrics.Bp2BuildMetrics
 	nil,                     // 1: soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
+	nil,                     // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
+	nil,                     // 3: soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
 }
 var file_bp2build_metrics_proto_depIdxs = []int32{
 	1, // 0: soong_build_bp2build_metrics.Bp2BuildMetrics.ruleClassCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
-	1, // [1:1] is the sub-list for method output_type
-	1, // [1:1] is the sub-list for method input_type
-	1, // [1:1] is the sub-list for extension type_name
-	1, // [1:1] is the sub-list for extension extendee
-	0, // [0:1] is the sub-list for field type_name
+	2, // 1: soong_build_bp2build_metrics.Bp2BuildMetrics.convertedModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
+	3, // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.totalModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
+	3, // [3:3] is the sub-list for method output_type
+	3, // [3:3] is the sub-list for method input_type
+	3, // [3:3] is the sub-list for extension type_name
+	3, // [3:3] is the sub-list for extension extendee
+	0, // [0:3] is the sub-list for field type_name
 }
 
 func init() { file_bp2build_metrics_proto_init() }
@@ -207,7 +254,7 @@
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_bp2build_metrics_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   2,
+			NumMessages:   4,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
index 5e88966..6d98a3d 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
@@ -32,4 +32,10 @@
 
   // List of converted modules
   repeated string convertedModules = 5;
+
+  // Counts of converted modules by module type.
+  map<string, uint64> convertedModuleTypeCount = 6;
+
+  // Counts of total modules by module type.
+  map<string, uint64> totalModuleTypeCount = 7;
 }
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index f1bb862..80f8c1a 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -32,12 +32,12 @@
 // of what an event is and how the metrics system is a stack based system.
 
 import (
-	"io/ioutil"
 	"os"
 	"runtime"
 	"strings"
 	"time"
 
+	"android/soong/shared"
 	"google.golang.org/protobuf/proto"
 
 	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
@@ -196,7 +196,7 @@
 	}
 	m.metrics.HostOs = proto.String(runtime.GOOS)
 
-	return save(&m.metrics, out)
+	return shared.Save(&m.metrics, out)
 }
 
 // SetSoongBuildMetrics sets the metrics collected from the soong_build
@@ -228,25 +228,5 @@
 
 // Dump saves the collected CUJs metrics to the raw protobuf file.
 func (c *CriticalUserJourneysMetrics) Dump(filename string) (err error) {
-	return save(&c.cujs, filename)
-}
-
-// save takes a protobuf message, marshals to an array of bytes
-// and is then saved to a file.
-func save(pb proto.Message, filename string) (err error) {
-	data, err := proto.Marshal(pb)
-	if err != nil {
-		return err
-	}
-
-	tempFilename := filename + ".tmp"
-	if err := ioutil.WriteFile(tempFilename, []byte(data), 0644 /* rw-r--r-- */); err != nil {
-		return err
-	}
-
-	if err := os.Rename(tempFilename, filename); err != nil {
-		return err
-	}
-
-	return nil
+	return shared.Save(&c.cujs, filename)
 }
diff --git a/ui/terminal/simple_status.go b/ui/terminal/simple_status.go
index 936b275..3157813 100644
--- a/ui/terminal/simple_status.go
+++ b/ui/terminal/simple_status.go
@@ -22,32 +22,41 @@
 )
 
 type simpleStatusOutput struct {
-	writer    io.Writer
-	formatter formatter
-	keepANSI  bool
+	writer      io.Writer
+	formatter   formatter
+	keepANSI    bool
+	outputLevel status.MsgLevel
 }
 
 // NewSimpleStatusOutput returns a StatusOutput that represents the
 // current build status similarly to Ninja's built-in terminal
 // output.
-func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool) status.StatusOutput {
+func NewSimpleStatusOutput(w io.Writer, formatter formatter, keepANSI bool, quietBuild bool) status.StatusOutput {
+	level := status.StatusLvl
+	if quietBuild {
+		level = status.PrintLvl
+	}
 	return &simpleStatusOutput{
-		writer:    w,
-		formatter: formatter,
-		keepANSI:  keepANSI,
+		writer:      w,
+		formatter:   formatter,
+		keepANSI:    keepANSI,
+		outputLevel: level,
 	}
 }
 
 func (s *simpleStatusOutput) Message(level status.MsgLevel, message string) {
-	if level >= status.StatusLvl {
+	if level >= s.outputLevel {
 		fmt.Fprintln(s.writer, s.formatter.message(level, message))
 	}
 }
 
-func (s *simpleStatusOutput) StartAction(action *status.Action, counts status.Counts) {
+func (s *simpleStatusOutput) StartAction(_ *status.Action, _ status.Counts) {
 }
 
 func (s *simpleStatusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
+	if s.outputLevel > status.StatusLvl {
+		return
+	}
 	str := result.Description
 	if str == "" {
 		str = result.Command
diff --git a/ui/terminal/status.go b/ui/terminal/status.go
index 2ad174f..ff0af47 100644
--- a/ui/terminal/status.go
+++ b/ui/terminal/status.go
@@ -29,9 +29,9 @@
 func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput {
 	formatter := newFormatter(statusFormat, quietBuild)
 
-	if !forceSimpleOutput && isSmartTerminal(w) {
-		return NewSmartStatusOutput(w, formatter)
+	if forceSimpleOutput || quietBuild || !isSmartTerminal(w) {
+		return NewSimpleStatusOutput(w, formatter, forceKeepANSI, quietBuild)
 	} else {
-		return NewSimpleStatusOutput(w, formatter, forceKeepANSI)
+		return NewSmartStatusOutput(w, formatter)
 	}
 }